Search code examples
c++language-lawyerc++17constexprstructured-bindings

If structured bindings cannot be constexpr why can they be used in constexpr function?


According to this answer apparently there is no good reason why structured bindings are not allowed to be constexpr, yet the standard still forbids it. In this case, however, shouldn't the use of the structured bindings inside the constexpr function also be prohibited? Consider a simple snippet:

#include <utility>

constexpr int foo(std::pair<int, int> p) {
    auto [a, b] = p;
    return a;
}

int main() {
    constexpr int a = foo({1, 2});
    static_assert(a == 1);
}

Both gcc and clang does not cause trouble compiling the code. Is the code ill-formed either way or is this one actually allowed?


Solution

  • In the case of function declaration, the constexpr specifier is an assertion made to the compiler that the function being declared may be evaluated in a constant expression, i.e. an expression that can be evaluated at compile-time. Nevertheless the object initialization inside a declaration does not need to have constexpr inside its declaration specifier to be a constant expression.

    Shorter: constexpr function may imply constant expression but constant expression initialization does not need that the associated declaration has a constexpr specifier.

    You can check this in the C++ standard [dcl.constexpr]:

    A call to a constexpr function produces the same result as a call to an equivalent non-constexpr function in all respects except that

    — a call to a constexpr function can appear in a constant expression[...]

    This is the evaluation of an expression that determines if an expression is a constant expression [expr.const]:

    An expression e is a core constant expression unless the evaluation of e [...] would evaluate one of the following expression[...]

    A declaration is not an expression, so an initialization of an object being declared is a constant expression irrespective of the presence or not of a constexpr specifier in the declaration.

    Finally, in [dcl.constexpr], it is specified that a constexpr function must be such that there exist parameters for which its body can be evaluated as a constant expression:

    For a constexpr function or constexpr constructor that is neither defaulted nor a template, if no argument values exist such that an invocation of the function or constructor could be an evaluated subexpression of a core constant expression (8.20), or, for a constructor, a constant initializer for some object (6.6.2), the program is ill-formed, no diagnostic required.

    When you declare constexpr int a the compiler expects a to be inialized by a constant expression and the expression foo({1,2}) is a constant expression, so your code is well formed.

    PS: Nevertheless, declaration specifiers (static, thread_local=>static) in the the declaration of function local variable implies that the function cannot be declared constexpr.