Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

`static constexpr` function called in a constant expression is...an error?

I have the following code:

class MyClass {   static constexpr bool foo() { return true; }   void bar() noexcept(foo()) { }     }; 

I would expect that since foo() is a static constexpr function, and since it's defined before bar is declared, this would be perfectly acceptable.

However, g++ gives me the following error:

 error: ‘static constexpr bool MyClass::foo()’ called in a constant expression 

This is...less than helpful, since the ability to call a function in a constant expression is the entire point of constexpr.

clang++ is a little more helpful. In addition to an error message stating that the argument to noexcept must be a constant expression, it says:

note: undefined function 'foo' cannot be used in a constant expression note: declared here static constexpr bool foo() { return true; }                       ^ 

So...is this a two-pass-compilation problem? Is the issue that the compiler is attempting to declare all the member functions in the class before any of them are defined? (Note that outside of the context of a class, neither compiler throws an error.) This surprises me; intuitively, I don't see any reason for static constexpr member functions not to be useable in any and all constant expressions, inside the class or out.

like image 423
Kyle Strand Avatar asked Apr 10 '15 00:04

Kyle Strand


People also ask

What does constexpr const mean?

constexpr creates a compile-time constant; const simply means that value cannot be changed.

What does static constexpr mean?

static defines the object's lifetime during execution; constexpr specifies that the object should be available during compilation. Compilation and execution are disjoint and discontiguous, both in time and space.

Is constexpr function static?

constexpr int a = 2; Static specifies the lifetime of the variable. A static constexpr variable has to be set at compilation, because its lifetime is the the whole program. Without the static keyword, the compiler isn't bound to set the value at compilation, and could decide to set it later.

Is constexpr constant?

The keyword constexpr was introduced in C++11 and improved in C++14. It means constant expression. Like const , it can be applied to variables: A compiler error is raised when any code attempts to modify the value.


Video Answer


1 Answers

As T.C. demonstrated with some links in a comment, the standard is not quite clear on this; a similar problem arises with trailing return types using decltype(memberfunction()).

The central problem is that class members are generally not considered to be declared until after the class in which they're declared is complete. Thus, regardless of the fact that foo is static constexpr and its declaration precedes that of bar, it cannot be considered "available" for use in a constant expression until MyClass is complete.

As pointed out by Shafik Yaghmour, there is some attempt within the standard to avoid a dependency on the ordering of members within a class, and obviously allowing the example in the original question to compile would introduce an ordering dependency (since foo would need to be declared before bar). However, there is already a minor dependency on ordering, because although constexpr functions can't be called inside noexcept, a noexcept expression itself might depend on an earlier declaration inside the class:

class MyClass {     // void bar() noexcept(noexcept(foo())); // ERROR if declared here     static constexpr bool foo();     void bar() noexcept(noexcept(foo())); // NO ERROR } 

(Note that this is not actually a violation of 3.3.7, since there is still only one correct program that is possible here.)

This behavior may actually be a violation of the standard; T.C. points out (in a comment below) that foo here should actually be looked up in the scope of the whole class. Both g++ 4.9.2 and clang++ 3.5.1 fail with an error when bar is declared first but compile with no errors or warnings when foo is declared first. EDIT: clang++ trunk-revision 238946 (from shortly before the release of 3.7.0) does not fail when bar is declared first; g++ 5.1 still fails.

Intriguingly, the following variation causes a "different exception specifier" with clang++ but not with g++:

class MyClass {   static constexpr bool foo2();   void bar2() noexcept(noexcept(foo2())); };  constexpr bool MyClass::foo2() { return true; } void MyClass::bar2() noexcept(noexcept(MyClass::foo2())) { } 

According to the error, the noexcept specification for the declaration of bar2 evaluates to noexcept(false), which is then considered a mismatch for noexcept(noexcept(MyClasss::foo2())).

like image 82
Kyle Strand Avatar answered Oct 08 '22 17:10

Kyle Strand