43

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.

13
  • 4
    open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1255; open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1626 Commented Apr 10, 2015 at 0:29
  • 1
    @MattMcNabb But the body of bar isn't complete before the noexcept expression that uses it in your example, so there's no inconsistency. Commented Apr 10, 2015 at 0:41
  • 1
    @T.C. I am puzzled by your ability to find these gems. You should write an answer, as this is highly non-obvious. Commented Apr 10, 2015 at 0:42
  • 1
    The links from T.C. say the standard says a constexpr can not be referenced until the class declaration is done (}). Commented Apr 10, 2015 at 0:43
  • 1
    @KyleStrand maybe my example wasn't so good. but currently there is no dependence on which order functions are defined; or which order they are declared in a class. Your proposal would introduce that, which could be a can of worms. The auto example in CWG 1255 illustrates one of the problems. Commented Apr 10, 2015 at 0:54

1 Answer 1

24

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

Sign up to request clarification or add additional context in comments.

11 Comments

That's a bit surprising. Names in exception-specifications are supposed to be looked up in the scope of the whole class ([basic.lookup.unqual]/p8, [basic.scope.class]/p1). I wonder if this is just the compilers at issue being nonconforming.
@T.C. ....Huh. So you're saying that the above code snippet should be valid regardless of the declaration order? Wouldn't that require some sort of two-pass declaration scheme?
You already have that with member function bodies and NSDMIs; the rule isn't exactly new.
@T.C. I'm not sure I immediately grasp the implications there. Could you give an example?
The standard says that you can do struct C { int *x = &y; int y; }; and of course struct C { int x() { return y; } int y; }; In both cases you are allowed to use a class member before its declaration, so obviously parsing of at least some part of the member declarations must be delayed until the class is complete.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.