Several times, in code review, I'm told to add contexpr to some named lambda closure declaration, i.e. I'm told to change this
auto lam = [capture list](args){body}
to this:
constexpr auto lam = [capture list](args){body}
Therefore, I'd like to understand what the conditions are that allow a lambda closure to be declared as constexpr, so I can make this change autonomously.
Here I read that
A constexpr variable must satisfy the following requirements:
its type must be a LiteralType.
it must be immediately initialized
the full-expression of its initialization, including all implicit conversions, constructors calls, etc, must be a constant expression
[a C++20-specific condtion]
Where I see that the second condition is verified, but I'd like some help understanding the first and especially the third condition.
Concerning the first condition, at the LiteralType page I understand that it is enough, for a variable to be a LiteralType, to be a possibly cv-qualified class type that has a destructor (how could it not have one?) and is a closure type (which is the case for the lam above), and all non-static data members and base classes are of non-volatile literal types (I'm not sure about this last part in relation to lambdas).
The bottom line is that I'd like to understand how I can understand, by inspection, if I can make a lambda constexpr.
The only thing that decides whether a lambda can be constexpr is the capture list. The body can be non-constexpr and a constexpr lambda can still have parameters like std::string, which are not literal types either.
its type must be a LiteralType.
The LiteralType requirement for lambdas effectively means that all captured members have to be non-volatile literal types too. Defining a lambda is equivalent to defining an anonymous struct where the captured variables are members.
constexpr int x = 0;
constexpr auto lam = [x](){};
// equivalent to:
struct Lam {
int x;
void operator()() {}
};
constexpr Lam lam2{x};
When we think of lambdas as such structs with call operators, it also becomes obvious what all these requirements mean. We can't capture a non-literal type, because that would require storing a non-literal type in this struct. And that can't happen in a constexpr context.
the full-expression of its initialization, including all implicit conversions, constructors calls, etc, must be a constant expression
Once again, this basically states that capturing variables has to happen in a constexpr way. All captured variables must be constexpr, otherwise the initialization won't be. In fact, constexpr variables are all implicitly captured. So what this really means is that a lambda can be made constexpr exactly when it doesn't have a capture list, or that capture list could be omitted.
Related
Recently I was surprised that the following code compiles in clang, gcc and msvc too (at least with their current versions).
struct A {
static const int value = 42;
};
constexpr int f(A a) { return a.value; }
void g() {
A a; // Intentionally non-constexpr.
constexpr int kInt = f(a);
}
My understanding was that the call to f is not constexpr because the argument i isn't, but it seems I am wrong. Is this a proper standard-supported code or some kind of compiler extension?
As mentioned in the comments, the rules for constant expressions do not generally require that every variable mentioned in the expression and whose lifetime began outside the expression evaluation is constexpr.
There is a (long) list of requirements that when not satisfied prevent an expression from being a constant expression. As long as none of them is violated, the expression is a constant expression.
The requirement that a used variable/object be constexpr is formally known as the object being usable in constant expressions (although the exact definition contains more detailed requirements and exceptions, see also linked cppreference page).
Looking at the list you can see that this property is required only in certain situations, namely only for variables/objects whose lifetime began outside the expression and if either a virtual function call is performed on it, a lvalue-to-rvalue conversion is performed on it or it is a reference variable named in the expression.
Neither of these cases apply here. There are no virtual functions involved and a is not a reference variable. Typically the lvalue-to-rvalue conversion causes the requirement to become important. An lvalue-to-rvalue conversions happens whenever you try to use the value stored in the object or one of its subobjects. However A is an empty class without any state and therefore there is no value to read. When passing a to the function, the implicit copy constructor is called to construct the parameter of f, but because the class is empty, it doesn't actually do anything. It doesn't access any state of a.
Note that, as mentioned above, the rules are stricter if you use references, e.g.
A a;
A& ar = a;
constexpr int kInt = f(ar);
will fail, because ar names a reference variable which is not usable in constant expressions. This will hopefully be fixed soon to be more consistent. (see https://github.com/cplusplus/papers/issues/973)
I'm searching for a way to identify empty (captureless) lambdas from other lambdas in a template function. I'm currently using C++17 but I'm curious for C++20 answers too.
My code looks like this:
template<typename T>
auto func(T lambda) {
// The aguments of the lambdas are unknown
if constexpr (/* is captureless */) {
// do stuff
}
}
Is it guaranteed by the C++ standard (17 or 20) that a captureless lambda, which is convertible to a function pointer, will also make std::is_empty yield true?
Take this code as an example:
auto a = []{}; // captureless
auto b = [c = 'z']{}; // has captures
static_assert(sizeof(a) == sizeof(b)); // Both are the same size
static_assert(!std::is_empty_v<decltype(b)>); // It has a `c` member
static_assert(std::is_empty_v<decltype(a)>); // Passes. It is guaranteed?
Live example
No, in fact, the standard explicitly grants permission for lambdas to have a size that doesn't line up with their declaration. [expr.prim.lambda.closure]/2 states
The closure type is declared in the smallest block scope, class scope, or namespace scope that contains the corresponding lambda-expression. [ Note: This determines the set of namespaces and classes associated with the closure type ([basic.lookup.argdep]). The parameter types of a lambda-declarator do not affect these associated namespaces and classes. — end note ] The closure type is not an aggregate type. An implementation may define the closure type differently from what is described below provided this does not alter the observable behavior of the program other than by changing:
the size and/or alignment of the closure type,
whether the closure type is trivially copyable ([class.prop]), or
(2.3)
whether the closure type is a standard-layout class ([class.prop]).
An implementation shall not add members of rvalue reference type to the closure type.
emphasis mine
So this allows the implementation to give the lambda a member even if it is capture-less. I don't think any implementation ever would, but they are legally allowed to do so.
From en.cppreference.com/w/cpp/language/initialization:
Unordered dynamic initialization, which [sic] applies only to (static/thread-local) class template static data members and variable templates (since C++14) that aren't explicitly specialized.
Therefore static templates appear to be vulnerable to an even worse version of The Static Initialization Order Fiasco (TSIOF) (i.e. unordered within a translation unit).
Does use of constexpr remove this vulnerability?
i.e. is the output of the below code guaranteed to be success?
Obviously, due to the nature of this question, working examples won't suffice as answers; quotes from the standard would be required.
(C++17 answers preferred)
#include<cassert>
template<class T> static constexpr T a = 41;
template<class T> static constexpr T b = a<T>+1;
int main(){
assert(b<int> == 42);
std::cout <<"success\n";
}
BTW, if someone is an expert on this I have a related, unanswered question (that would be easy for such an expert to answer) here.
Further, what would be the implications here if the answer to my other question is negative (i.e. constexpr doesn't help across translation units)?
UPDATE: I need to clarify what my concern is here. The original question title asked whether initialization order is a concern for constexpr template variables. I have clarified it. I am not concerned about whether dynamic initialization is taking place in the example; it isn't. My concern is that since ordered initialization cannot be assumed in the dynamic initialization case, can it be assumed in the constant initialization case? Prior to seeing the behavior of dynamically initialized template variables (within the same translation unit) I would have never even thought of this. However, since dynamically-initialized, static-duration template variables do not provide ordered initialization I now see no reason to assume that constant-initiliazed, static-duration template variables have guaranteed ordered initialization either. I need to be 100% sure that constant initialization of template variables takes place in the order of their definition within a TU.
Again, I see no reason to assume the constant-initializer-within-the-compiler is required to intialize in order if the dynamic initializer isn't. Absence of a warning in the standard that constant initialization is unordered does not suffice.
I realize that some may think this is excessive concern but I am working on safety-critical software and my company has placed adoption of C++14 on hold until this issue is resolved.
Based on basic.start.static:
Constant initialization is performed if a variable or temporary
object with static or thread storage duration is initialized by a
constant initializer for the entity.
In your code:
template<class T> static constexpr T a = 41; // constant initialization
is doing constant initialization which makes:
template<class T> static constexpr T b = a<T>+1;
be initialized with 42 due to the templates' constant evaluation.
which states that (From expr.const/8.7):
a variable whose name appears as a potentially constant evaluated
expression that is either a constexpr variable or is of non-volatile
const-qualified integral type or of reference type.
Thus, it is guaranteed the output is always "success"ful.
NOTE From basic.start.static/2:
Together, zero-initialization and constant initialization are called
static initialization
-- not dynamic initialization
[Note that throughout this post, a<T> and b<T> are shortened to a and b, respectively.]
[expr.const/3] states that:
A variable is usable in constant expressions after its initializing declaration is encountered if it is a constexpr variable, or it is of reference type or of const-qualified integral or enumeration type, and its initializer is a constant initializer.
Thus, a will be usable during b's initialisation, due to the constexpr specifier. constexpr is fully applicable both for variables and variable templates, as stated in the first sentence of [dcl.constexpr/1].
The constexpr specifier shall be applied only to the definition of a variable or variable template or the declaration of a function or function template.
Thus, a variable or variable template declared constexpr is usable in constant expressions (including the initialisation of other constexpr variables) after its own initialisation, with the value specified in its initialisation (due to constant initialisation being implictly guaranteed by constexpr).
If we wish to explore the second option in [expr.const/3] rather than relying on the "constexpr means yes" clause, that one will also lead to a being usable during b's initialisation, albeit by a more circuitous route that involves delving into multiple parts of the standard and piecing together the implicit guarantees.
First, would come the question of whether a has a constant initializer. To determine whether it has a constant initializer, we can consult [expr.const/2], which reads (note omitted):
A constant initializer for a variable or temporary object o is an initializer for which interpreting its full-expression as a constant-expression results in a constant expression, except that if o is an object, such an initializer may also invoke constexpr constructors for o and its subobjects even if those objects are of non-literal class types.
We have two explicit guarantees that the initialiser's full-expression is a constant expression, both from [dcl.constexpr/9]: a is implicitly const, and the full-expression not being a constant expression would be an error.
A constexpr specifier used in an object declaration declares the object as const.
Such an object shall have literal type and shall be initialized.
In any constexpr variable declaration, the full-expression of the initialization shall be a constant expression.
This, by extension, also means that (as of C++14), a will not be subject zero initialisation ([basic.start.static/2], irrelevant parts omitted):
Constant initialization is performed if a variable or temporary object with static or thread storage duration is initialized by a constant initializer ([expr.const]) for the entity.
If constant initialization is not performed, a variable with static storage duration ([basic.stc.static]) or thread storage duration ([basic.stc.thread]) is zero-initialized ([dcl.init]).
To verify that a has the correct value after initialisation, if we want to be really thorough, we could then look at [intro.execution/9]:
Every value computation and side effect associated with a full-expression is sequenced before every value computation and side effect associated with the next full-expression to be evaluated.
As we know that a's initialisation is a full-expression, we are implicitly guaranteed that a will have been assigned the value 41 by the end of its full-expression, before the next full-expression (including other initialisations) in sequence is evaluated. By combining this with [expr.const/3] and [basic.start.static/2], we are thus guaranteed that (as with the constexpr clause), a is usable in constant expressions encountered after its own initialisation, such as b's initialisation, and also guaranteed that a == 41 && a != 0.
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?
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.
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.
Unfortunately, I am somewhat confused about constexpr, global constants declared in header files, and the odr.
In short: Can we conclude from here
https://isocpp.org/files/papers/n4147.pdf
that
constexpr MyClass const MyClassObj () { return MyClass {}; }
constexpr char const * Hello () { return "Hello"; }
is preferable over
constexpr MyClass const kMyClassObj = MyClass {};
constexpr char const * kHello = "Hello";
for defining globals in a header file
if I want to "just use" those globally declared/defined entities and do not want to think about how I use them?
Note: as of C++17, you can declare your variables as inline.
TL;DR: If you want to be on the (very) safe side, go with constexpr functions. It isn't inherently necessary though, and certainly won't be if you're performing trivial operations on these objects and are solely interested in their value, or simply don't use them in the dangerous scenarios listed below.
The fundamental issue is that const variables at namespace scope such as yours (generally) have internal linkage ([basic.link]/(3.2)). This implies that each translation unit compiling the corresponding header will observe a different entity (i.e. symbol).
Now imagine we have a template or inline function in a header using those objects. The ODR is very precise about this scenario - [basic.def.odr]/6:
"initialized with a constant expression" is certainly met, since we're talking constexpr. So is "the object has the same value in all definitions of D" if you don't monkey about.
"the object isn't odr-used" is probably the only questionable condition. Basically, it requires that you don't necessitate the variables runtime existence as a symbol, which in turn implies that
You don't bind it to a reference (=> you don't forward it!)
You don't (neither explicitly nor implicitly) take its address.
The only exception to the second rule are arrays, which can be taken the address of implicitly inside a subscript operation as long as the two above rules aren't violated for the yielded glvalue.
More precisely, odr-use is governed by [basic.def.odr]/3:
A variable x whose name appears as a potentially-evaluated expression ex is odr-used by ex unless applying
the lvalue-to-rvalue conversion (4.1) to x yields a constant expression (5.20) that does not invoke any non-trivial functions and, if x is an object, ex is an element of the set of potential results of an expression e, where either the lvalue-to-rvalue conversion (4.1) is applied to e, or e is a discarded-value expression (Clause
5).
Applying l-t-r to any constexpr variable will behave as required by the first part. The second part requires that the variable be used as a value rather than an actual object; that is, it's eventually either discarded or directly evaluated, giving the above rules of thumb.
If you avoid odr-use of the variable inside inline functions, templates or the like, you're fine. But if you use the return value of a corresponding constexpr function, you won't have to worry, since prvalues are already behaving more like values/literals (not objects) and constexpr functions are inline and definitely won't violate the ODR (if you don't use constexpr variables inside there!).