C ++ 17中具有非固定基础类型的constexpr静态从int强制转换为作用域枚举的未定义行为 [英] Undefined behavior of constexpr static cast from int to scoped enum with non-fixed underlying type compiles in C++17
问题描述
我想知道以下内容是否应该在C ++ 17中编译
枚举类E{A,B};constexpr E x = static_cast< E>(2);
这将在Ubuntu 20.04上同时使用GCC 9.3.0和Clang 10.0.0进行编译.
我的问题是
- 应该编译吗?
- 如果应该,为什么?
我认为不应该.关于未定义行为(UB)和枚举的文章很多,但我找不到在常量表达式上下文中引起它的任何文章.另外,大多数帖子使用基础类型,我认为范围枚举没有任何固定的基础类型.由于我无权访问ISO标准的副本,因此我将参考最新的C ++ 17草案,该草案找到了cppreference.com ,他们在constexpr中讨论了在使用UB的情况下是否需要编译器发出诊断:该职位的TLDR是:是,但是警告可能足以引起对该标准的投诉.
作用域枚举始终具有固定的基础类型.[dcl.enum]/5(C ++ 17):
对于作用域枚举类型,如果未明确指定,则基础类型为
int
.在这两种情况下,底层类型都被称为是 fixed .
因此,您的 E
具有固定的 int
基础类型.然后在第8段中:
对于基础类型固定的枚举,该枚举的值为基础类型的值.
2
在 int
的范围内,因此,根据您引用[expr.static.cast]的文本,演员表的行为得到了明确定义./p>
I wonder if the following should or should not compile in C++17
enum class E
{
A, B
};
constexpr E x = static_cast<E>(2);
This compiles with both GCC 9.3.0 and Clang 10.0.0 on Ubuntu 20.04.
My question is
- Should this compile?
- If it should, why?
I don’t think it should. There are many posts regarding undefined behaviour (UB) and enums, but I couldn’t find any that brought it up in a constant expression context. Also, most posts use an underlying type, I consider scoped enums without any fixed underlying type. Since I don’t have access to a copy of an ISO standard, I will refer to the latest C++17 draft found a cppreference.com here in the reasoning below.
First, we find the paragraph that discards expressions with UB from being constant expressions
[expr.const]/2: An expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine, would evaluate one of the following expressions:
with subsection
[expr.const]/2.6: an operation that would have undefined behavior as specified in Clauses [intro] through [cpp] of this International Standard [ Note: including, for example, signed integer overflow (Clause [expr]), certain pointer arithmetic ([expr.add]), division by zero, or certain shift operations — end note ] ;
which tells us that constant expressions may not contain UB.
Then, we find the section regarding static casts from int to enum
[expr.static.cast]/10: A value of integral or enumeration type can be explicitly converted to a complete enumeration type. The value is unchanged if the original value is within the range of the enumeration values ([dcl.enum]). Otherwise, the behavior is undefined. A value of floating-point type can also be explicitly converted to an enumeration type. The resulting value is the same as converting the original value to the underlying type of the enumeration ([conv.fpint]), and subsequently to the enumeration type.
which tells us that the result of a static cast is well defined if the operand is within the range of the target enum. The section refers to [decl.enum]/8 regarding the range of the enumeration values (too long to post here, I can't get the formatting right either). Anyways, it says that the range of valid values of an enum of non-fixed underlying type is defined by the smallest bitset that can fit all values between the smallest and the biggest enumeration (in two-complements format).
Finally, applying these three sections on the example code, we can say that the smallest bitfield that can contain both A = 0 and B = 1 is of size one. Hence, the integer 2 (which requires two bits to represent in two-complements format) is outside the range of the target enum E, and the static cast has undefined behaviour. Since a constexpr variable can’t have undefined behaviour, the program should not compile.
The closest I could get to the answer is in this post where they discuss whether the compiler is required to issue a diagnostic in the case of UB in constexpr: TLDR of that post is: Yes, but a warning might suffice to be complaint with the standard.
Scoped enums always have fixed underlying type. [dcl.enum]/5 (C++17):
For a scoped enumeration type, the underlying type is
int
if it is not explicitly specified. In both of these cases, the underlying type is said to be fixed.
So your E
has fixed underlying type of int
. Then in paragraph 8:
For an enumeration whose underlying type is fixed, the values of the enumeration are the values of the underlying type.
2
is in range for int
, so by the text you quoted from [expr.static.cast], the behaviour of the cast is well-defined.
这篇关于C ++ 17中具有非固定基础类型的constexpr静态从int强制转换为作用域枚举的未定义行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!