C ++ 17中具有非固定基础类型的constexpr静态从int强制转换为作用域枚举的未定义行为 [英] Undefined behavior of constexpr static cast from int to scoped enum with non-fixed underlying type compiles in C++17

查看:38
本文介绍了C ++ 17中具有非固定基础类型的constexpr静态从int强制转换为作用域枚举的未定义行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想知道以下内容是否应该在C ++ 17中编译

 枚举类E{A,B};constexpr E x = static_cast< E>(2); 

这将在Ubuntu 20.04上同时使用GCC 9.3.0和Clang 10.0.0进行编译.

我的问题是

  1. 应该编译吗?
  2. 如果应该,为什么?

我认为不应该.关于未定义行为(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

  1. Should this compile?
  2. 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆