Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Forward-declaration of a `constexpr` function inside another function -- Compiler bug?

While producing a MCVE for this problem I stumbled upon, I've found the following discrepancy between compilers:

Consider the following code :

// constexpr int f(); // 1

constexpr int g() {
    constexpr int f(); // 2
    return f();
}

constexpr int f() {
    return 42;
}

int main() {
    constexpr int i = g();
    return i;
}

This code compiles on Clang 3.8.0, but fails on GCC 6.1.0 with:

error: 'constexpr int f()' used before its definition

Commenting out // 2 and uncommenting // 1 works on both compilers.

Interestingly, moving f's definition in place of // 1 compiles, but triggers a warning at // 2:

warning: inline function 'constexpr int f()' used but never defined

Which compiler is right ?

like image 858
Quentin Avatar asked May 30 '16 13:05

Quentin


People also ask

Why constexpr Cannot be forward declared?

Although constexpr variables can be given external linkage via the extern keyword, they can not be forward declared, so there is no value in giving them external linkage. This is because the compiler needs to know the value of the constexpr variable (at compile time).

Are constexpr functions evaluated at compile time?

A constexpr function that is eligible to be evaluated at compile-time will only be evaluated at compile-time if the return value is used where a constant expression is required. Otherwise, compile-time evaluation is not guaranteed.

Can constexpr be extern?

Actually, extern constexpr does make sense, and would be useful. In particular, static constexpr class member variables automatically have external linkage (which is a huge inconsistency and gotcha - people often forget to add a definition in a . cc file).

Is constexpr automatically inline?

Yes ([dcl. constexpr], §7.1. 5/2 in the C++11 standard): "constexpr functions and constexpr constructors are implicitly inline (7.1.


1 Answers

Replacing the constexpr functions with inline functions retains the exact same problem (it is okay with the global declaration 1, but not with the function-scope declaration 2.) Since constexpr implies inline this seems like the cause.

In this case, with declaration 2, GCC complains: warning: 'inline' specifier invalid for function 'f' declared out of global scope and warning: inline function 'int f()' used but never defined. It fails to link ("undefined reference to 'f()'").

So it looks like it gives up on inlining, puts in a call, but doesn't bother emitting code for f() because all uses are inlined (?), so the link fails.

and Clang complains: error: inline declaration of 'f' not allowed in block scope

Since constexpr implies inline, it seems that this rule that inline declarations are not allowed in block scope should also apply to constexpr, and so GCC is correct. But the standard does not seem to come out and say this. In the draft I examined, the rule about inline is in §7.1.2 [dcl.fct.spec], part 3: "The inline specifier shall not appear on a block scope function declaration", but nothing similar appears about constexpr.

like image 74
Nick Matteo Avatar answered Oct 10 '22 04:10

Nick Matteo