Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 811
W.F. Avatar asked Sep 16 '17 12:09

W.F.


People also ask

Can struct be constexpr?

Since these structs are pure data, they can be constexpr.

When can a function be constexpr?

constexpr functions A constexpr function is one whose return value is computable at compile time when consuming code requires it. Consuming code requires the return value at compile time to initialize a constexpr variable, or to provide a non-type template argument.

Why constexpr instead of define?

#define directives create macro substitution, while constexpr variables are special type of variables. They literally have nothing in common beside the fact that before constexpr (or even const ) variables were available, macros were sometimes used when currently constexpr variable can be used.

Is constexpr can be used with #define macros?

Absolutely not. Not even close. Apart from the fact your macro is an int and your constexpr unsigned is an unsigned , there are important differences and macros only have one advantage.


2 Answers

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.

like image 76
Oliv Avatar answered Sep 28 '22 00:09

Oliv


There are several requirements that a constexpr function must meet. There are some requirements for the body of a constexpr function, and the shown code does not appear to violate any of them. The key point is that there is no requirement that every statement in a function must be a constexpr. The only interesting requirement in question here, is this one:

there exists at least one set of argument values such that an invocation of the function could be an evaluated subexpression of a core constant expression (for constructors, use in a constant initializer is sufficient) (since C++14). No diagnostic is required for a violation of this bullet.

Note the last sentence. The compiler may, but is not required to, throw a red flag.

The key requirement is merely that there is some assortment of parameter values to the function that results in a constant result from the function (and the function body meets the listed requirements). For example, the function might use a structured binding conditionally; but for some set of parameter values do something else, producing a constant result. This would tick this checkbox for a constexpr function.

But, despite the sophistication of modern C++ compilers, they may not necessarily be capable of reaching this determination in every possible instance, so, in practice, it would be hard to enforce such a requirement, hence the compilers are permitted to just take this for granted.

like image 43
Sam Varshavchik Avatar answered Sep 28 '22 02:09

Sam Varshavchik