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.
constexpr creates a compile-time constant; const simply means that value cannot be changed.
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.
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.
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.
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()))
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With