我在g++4.8.1和clang++3.4的行为之间存在差异。
我有一个文本类型的类A
,它有一个explicit
constexpr
到类型enum class E
的转换函数。
Gcc允许我在某些情况下使用转换函数从A
类型的常量表达式初始化E
类型的constexpr
变量,但当变量是静态类成员(下面的e2
)
Clang在所有上下文(e1
、e2
和e3
)中拒绝初始化。
根据[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拒绝s1
、s2
和s3
的初始化。根据[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
哪一个编译器是正确的?
编辑:需要注意的几件有趣的事情:
- clang-3.4和clang-svn的结果不同,请参阅下面的评论
- 当使用parens而不是大括号进行初始化时,
e2
/s2
和e1
/e3
/s1
/s3
之间仍然存在差异,请参见http://coliru.stacked-crooked.com/a/daca396a63425c6b.gcc和clang-svn同意,但我不相信拒绝e2和s2是正确的
原因是C++11标准中存在一个错误,其中{}
不适用于复制构造函数。这就是为什么()
构造函数有效,而{}
构造函数无效。
Bjarne Stroustrup在他的书的勘误表下说,它是在C++14 中修复的