Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

inline constexpr function definition legal or not? gcc (ok) vs clang (error)

My current program is rejected by clang but compiles fine with gcc. It boils down to the following simplified example:

struct A {
  static constexpr inline int one();
};

inline constexpr int A::one() { return 1; }

int main() {
  return 0;
}

g++ 4.7.2 compiles it without errors (g++ -std=c++11 -Wall -g -o main example.cpp). clang++ 3.1 rejects it:

$ clang++ -std=c++11 -Wall -g -o main example.cpp 
example.cpp:6:25: error: conflicting types for 'one'
inline constexpr int A::one() { return 1; }
                        ^
example.cpp:3:31: note: previous declaration is here
  static constexpr inline int one();
                              ^
1 error generated.

My bet is that gcc is right and and clang is wrong? The program should be legal C++11.

Interesting sidenote. If one is implemented within the struct, clang no longer complains:

struct A {
  static constexpr inline int one() { return 1; }
}

gcc also accepts this variant. From my understanding, both versions should be identical according to the standard. Is it a clang bug or am I missing something?

like image 685
Philipp Claßen Avatar asked Dec 17 '12 01:12

Philipp Claßen


People also ask

Is constexpr function inline?

A constexpr specifier used in a function or static data member (since C++17) declaration implies inline .

Is constexpr automatically inline?

A constexpr function or constructor is implicitly inline .

Does constexpr improve performance?

Understanding constexpr Specifier in C++ constexpr is a feature added in C++ 11. The main idea is a performance improvement of programs by doing computations at compile time rather than run time. Note that once a program is compiled and finalized by the developer, it is run multiple times by users.

Should I use constexpr everywhere?

Yes. I believe putting such const ness is always a good practice wherever you can. For example in your class if a given method is not modifying any member then you always tend to put a const keyword in the end.


1 Answers

This was a Clang bug (fixed in Clang 3.2). The problem was that Clang wasn't correctly handling the impact of implicit constness when determining whether a redeclaration of a function matched a prior declaration. Consider:

struct A {
  int f();                  // #1
  constexpr int f() const;  // #2 (const is implicit in C++11 and can be omitted)
  static constexpr int g(); // #3
};

int A::f() { return 1; }           // #4, matches #1
constexpr int A::f() { return 1; } // #5, matches #2, implicitly const
constexpr int A::g() { return 1; } // #6, matches #3, not implicitly const

When matching the out-of-class declaration #5 against members of A, the compiler has a problem: it doesn't know what type the new declaration of A::f has yet. If A::f is a non-static member function, then its type is int () const, and if it's a static member function then its type is int () (no implicit const).

Clang 3.1 didn't get this entirely right: it assumed that if a constexpr function were a member function then the constexpr made it implicitly const, which allows #4 and #5 to work, but breaks #6. Clang 3.2 fixes this by implementing the constexpr-implies-const rule twice: once in redeclaration matching (such that #5 is considered to redeclare #2 and not #1, even though it isn't yet implicitly const), and again once the prior declaration has been chosen (to add the implicit const to #5).

like image 188
Richard Smith Avatar answered Oct 07 '22 00:10

Richard Smith