Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I have a discrepancy between the behaviour of g++ 4.8.1 and clang++ 3.4.

I've got a class A, of literal type, that has an explicit constexpr conversion function to type enum class E.

Gcc allows me to initialize constexpr variables of type E from a constant expression of type A using the conversion function in some cases, but not when the variable is a static class member (e2 below)

Clang rejects the initialization in all contexts (e1, e2 and e3).

According to [over.match.conv]p1 use of an explicit conversion function is OK here

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

I see a similar pattern when converting to another literal class type instead of an enum type - g++ rejects the initialization of s1, clang rejects the initialization of s1, s2 and s3. I think these should be valid as well, as per [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

Which compiler, if either, is right?


Edit: A couple of interesting things to note:

  1. The results are different between clang-3.4 and clang-svn, see comments below.
  2. When using parens for the initialization instead of braces, there is still a difference between e2/s2 and e1/e3/s1/s3, see http://coliru.stacked-crooked.com/a/daca396a63425c6b. gcc and clang-svn agree, but I'm not convinced that rejecting e2 and s2 is correct.
like image 328
je4d Avatar asked Jan 07 '14 13:01

je4d


1 Answers

Strangely enough, Clang appears to be correct in rejecting these.

The reason is that there is a bug in the C++11 standard in which {} doesn't work for copy constructors. This is why () constructors work, but {} constructors don't.

Bjarne Stroustrup says under the errata for his book that it's fixed in C++14

like image 70
Edward Avatar answered Oct 22 '22 03:10

Edward