Philipp Claßen
Philipp Claßen

Reputation: 43969

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?

Upvotes: 16

Views: 3366

Answers (3)

Richard Smith
Richard Smith

Reputation: 14158

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).

Upvotes: 10

ecatmur
ecatmur

Reputation: 157334

Although the standard doesn't explicitly mention whether the definition of a constexpr static member function is allowed to be separate from its declaration, it has the following example of a separate definition of a constexpr constructor, under 7.1.5p1:

struct pixel {
  int x;
  int y;
  constexpr pixel(int); // OK: declaration
};
constexpr pixel::pixel(int a)
  : x(square(a)), y(square(a)) // OK: definition
  { }

So it seems clear that constexpr functions can have separate declaration and definition. Also in 7.1.5p1:

If any declaration of a function or function template has constexpr specifier, then all its declarations shall contain the constexpr specifier.

This implies that a constexpr function can have (multiple) non-definition declarations.

Upvotes: 8

Nathan Binkert
Nathan Binkert

Reputation: 9124

I'm pretty sure that g++ is correct. In fact this used to be a bug in g++. I can't find a place in the standard that explicitly says that you can have a static constexpr declaration separate from the definition, but if you look in section 7.1.5 that talks about the constexpr specifier (summarized here), it doesn't rule it out, which generally means that it is allowed.

Upvotes: 2

Related Questions