通过显式转换函数初始化枚举类类型的静态constexpr类成员

Initialization of a static constexpr class member of enum-class type by explicit conversion function

本文关键字:类型 constexpr 成员 静态 枚举 显式转换 函数 初始化      更新时间:2023-10-16

我在g++4.8.1和clang++3.4的行为之间存在差异。

我有一个文本类型的类A,它有一个explicitconstexpr到类型enum class E的转换函数。

Gcc允许我在某些情况下使用转换函数从A类型的常量表达式初始化E类型的constexpr变量,但当变量是静态类成员(下面的e2)

Clang在所有上下文(e1e2e3)中拒绝初始化。

根据[over.match.conv]p1,这里可以使用显式转换函数

enum class E { e };
struct A { explicit constexpr operator const E() const noexcept { return E::e; } };
constexpr E e1{A{}};                      // Gcc: OK, Clang: Error
struct B { static constexpr E e2{A{}}; }; // Gcc: Error, Clang: Error
void f() { static constexpr E e3{A{}}; }  // Gcc: OK, Clang: Error

当转换到另一个文本类类型而不是枚举类型时,我看到了类似的模式-g++拒绝s1的初始化,clang拒绝s1s2s3的初始化。根据[over.match.copy]p1,我认为这些也应该是有效的。

struct S { constexpr S(){} constexpr S(const S&){}};
struct A { explicit constexpr operator S() const noexcept { return S(); } };
constexpr S s1{A{}};                      // Gcc: OK, Clang: Error
struct B { static constexpr S s2{A{}}; }; // Gcc: Error, Clang: Error
void f() { static constexpr S s3{A{}}; }  // Gcc: OK, Clang: Error

哪一个编译器是正确的?


编辑:需要注意的几件有趣的事情:

  1. clang-3.4和clang-svn的结果不同,请参阅下面的评论
  2. 当使用parens而不是大括号进行初始化时,e2/s2e1/e3/s1/s3之间仍然存在差异,请参见http://coliru.stacked-crooked.com/a/daca396a63425c6b.gcc和clang-svn同意,但我不相信拒绝e2和s2是正确的
奇怪的是,Clang似乎正确地拒绝了这些。

原因是C++11标准中存在一个错误,其中{}不适用于复制构造函数。这就是为什么()构造函数有效,而{}构造函数无效。

Bjarne Stroustrup在他的书的勘误表下说,它是在C++14 中修复的