Can I use a constexpr value in a lambda without capturing it? - c++

I would want to use a constexpr value in a lambda. Reading the answer to
Using lambda captured constexpr value as an array dimension, I assumed the following should work:
#include<array>
int main()
{
constexpr int i = 0;
auto f = []{
std::array<int, i> a;
};
return 0;
}
However, Clang 3.8 (with std=c++14) complains that
variable 'i' cannot be implicitly captured in a lambda with no
capture-default specified
Should this be considered a bug in clang 3.8?
BTW:
The above code does compile with gcc 4.9.2.
If I change the lambda expresion to capture explicitly:
...
auto f = [i]{
...
clang 3.8 compiles it, but gcc 4.9.2 fails:
error: the value of ‘i’ is not usable in a constant expression
...

Should this be considered a bug in clang 3.8?
Yep. A capture is only needed if [expr.prim.lambda]/12 mandates so:
Note in particular the highlighted example. f(x) does not necessitate x to be captured, because it isn't odr-used (overload resolution selects the overload with the object parameter). The same argumentation applies to your code - [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…
This requirement is certainly met.
…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).
i is its set of potential results as per [basic.def.odr]/(2.1), and the l-t-r conversion is indeed immediately applied as its passed to a non-type template parameter of object type.
Hence, as we have shown that (12.1) isn't applicable - and (12.2) clearly isn't, either - Clang is wrong in rejecting your snippet.

Related

lambdas in Clang and MSVC require to capture object not ODR-used [duplicate]

I would want to use a constexpr value in a lambda. Reading the answer to
Using lambda captured constexpr value as an array dimension, I assumed the following should work:
#include<array>
int main()
{
constexpr int i = 0;
auto f = []{
std::array<int, i> a;
};
return 0;
}
However, Clang 3.8 (with std=c++14) complains that
variable 'i' cannot be implicitly captured in a lambda with no
capture-default specified
Should this be considered a bug in clang 3.8?
BTW:
The above code does compile with gcc 4.9.2.
If I change the lambda expresion to capture explicitly:
...
auto f = [i]{
...
clang 3.8 compiles it, but gcc 4.9.2 fails:
error: the value of ‘i’ is not usable in a constant expression
...
Should this be considered a bug in clang 3.8?
Yep. A capture is only needed if [expr.prim.lambda]/12 mandates so:
Note in particular the highlighted example. f(x) does not necessitate x to be captured, because it isn't odr-used (overload resolution selects the overload with the object parameter). The same argumentation applies to your code - [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…
This requirement is certainly met.
…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).
i is its set of potential results as per [basic.def.odr]/(2.1), and the l-t-r conversion is indeed immediately applied as its passed to a non-type template parameter of object type.
Hence, as we have shown that (12.1) isn't applicable - and (12.2) clearly isn't, either - Clang is wrong in rejecting your snippet.

Generic lambda and its argument as constant expression

The following code is accepted by GCC 7.2 and clang 5.0.0, but is rejected by Microsoft VS 2017 15.5.0 Preview 5 and Intel C++ compiler 19:
struct S { };
constexpr int f(S)
{
return 0;
}
int main()
{
auto lambda = [](auto x)
{
constexpr int e = f(x);
};
lambda(S{});
}
Microsoft:
<source>(12): error C2131: expression did not evaluate to a constant
Intel:
<source>(12): error: expression must have a constant value
constexpr int e = f(x);
^
<source>(12): note: the value of parameter "x" (declared at line 10) cannot be used as a constant
constexpr int e = f(x);
^
If I replace f(x) with f(decltype(x){}), both Microsoft and Intel do not complain. I understand that x is not a constant expression, but it is not used inside f. This is probably why GCC and clang do not complain.
I guess that Microsoft and Intel compilers are correct in rejecting this code. What do you think?
From [expr.const]:
An expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine, would evaluate one of the following expressions:
[...]
an lvalue-to-rvalue conversion unless it is applied to
a non-volatile glvalue of integral or enumeration type that refers to a complete non-volatile const object with a preceding initialization, initialized with a constant expression, or
a non-volatile glvalue that refers to a subobject of a string literal, or
a non-volatile glvalue that refers to a non-volatile object defined with constexpr, or that refers to a non-mutable subobject of such an object, or
a non-volatile glvalue of literal type that refers to a non-volatile object whose lifetime began within the evaluation of e;
[...]
In f(x), we do an lvalue-to-rvalue conversion on x. x isn't of integral or enumeration type, it's not a subobject of a string-literal, it's not an object defined with constexpr, and its lifetime did not begin with the evaluation of f(x).
That seems to make this not a core constant expression.
However, as Casey points out, since S is empty, nothing in its implicitly-generated copy constructor would actually trigger this lvalue-to-rvalue conversion. That would mean that nothing in this expression actually violates any of the core constant expression restrictions, and hence gcc and clang are correct in accepting it. This interpretation seems correct to me. constexpr is fun.
This is not a gcc/clang bug. The same behavior can be reproduced in C++11 with a template function:
template <typename T>
void foo(T x)
{
constexpr int e = f(x);
}
int main()
{
foo(S{});
}
on godbolt.org
The question is, given...
template <typename T>
void foo(T x)
{
constexpr int e = f(x);
}
...is f(x) a constant expression?
From [expr.const]:
An expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine, would evaluate one of the following expressions:
invocation of a function other than a constexpr constructor for a literal class, a constexpr function, or an implicit invocation of a trivial destructor
S{} and 0 are constant expressions because it doesn't violate any of the rules in [expr.const]. f(x) is a constant expression because it's an invocation to a constexpr function.
Unless I am missing something, gcc and clang are correct here.

Calling a constexpr method through a reference - is the result a constant expression?

The following code
#include <array>
void foo(const std::array<int, 42> &a)
{
constexpr size_t S = a.size();
}
int main() {}
compiles fine in GCC, but fails to compile in clang with the following error message
main.cpp:5:28: error: constexpr variable 'S' must be initialized by a constant expression
constexpr size_t S = a.size();
^~~~~~~~
Meanwhile, many posts about constexpr issues on SO seem to imply that clang often has better (more pedantic?) support for constexpr. So, which compiler would be correct in this case?
Note that both compilers gladly accept the code once the reference parameter is replaced with pass-by-value parameter.
[expr.const]/2:
A conditional-expression e is a core constant expression unless
the evaluation of e, following the rules of the abstract machine
([intro.execution]), would evaluate one of the following expressions:
[...]
an id-expression that refers to a variable or data member of reference type unless the reference has a preceding initialization and either
it is initialized with a constant expression or
its lifetime began within the evaluation of e;
[...]
Evaluating a.size() evaluates the id-expression a, which "refers to a variable...of reference type" and has no preceding initialization. It is therefore not a core constant expression and so not a constant expression.

undefined reference when accessing static constexpr float member

This code works:
struct Blob {
static constexpr int a = 10;
};
int main() {
Blob b;
auto c = b.a;
}
But if I change int to float I get an error:
struct Blob {
static constexpr float a = 10.0f;
};
/tmp/main-272d80.o: In function main': main.cpp:(.text+0xe):
undefined reference toBlob::a'
Why can't I use a constexpr float in that way?
Compiler:
Ubuntu clang version 3.5.0-4ubuntu2 (tags/RELEASE_350/final)
Tested on gcc version 4.9.1 (Ubuntu 4.9.1-16ubuntu6) and there were no error.
EDIT:
It will compile if I use -O1, -O2, -O3 or -Os but fails with -O0
C++11 reads
A variable whose name appears as a potentially-evaluated expression is
odr-used unless it is an object that satisfies the requirements for
appearing in a constant expression (5.19) and the lvalue-to-rvalue
conversion (4.1) is immediately applied.
Clearly the l-t-r conversion is immediately applied, and a constexpr variable of floating point type can appear in constant expressions as per [expr.const]/(2.7.1):
A conditional-expression is a core constant expression unless it
involves one of the following as a potentially evaluated subexpression
[..]
an lvalue-to-rvalue conversion (4.1) unless it is applied to
a glvalue of literal type that refers to a non-volatile object defined with constexpr, or that refers to a sub-object of such an
object, or
Seems to be a Clang bug.
Interestingly, if we use Blob::a instead, clang does not complain:
auto c = Blob::a;
This should not matter for determining if the it is odr-used or not. So this looks like a clang bug which I can reproduce on clang 3.7 using no optimization only. We can tell this is an odr issue since adding a out of class definition fixes the issue (see it live):
constexpr float Blob::a ;
So when do you need to define a static constexpr class member? This is covered in section 9.4.2 [class.static.data] which says (emphasis mine going forward):
A static data member of literal type can be declared in the
class definition with the constexpr specifier; if so, its declaration shall specify a brace-or-equal-initializer
in which every initializer-clause that is an assignment-expression is a constant expression. [ Note: In both
these cases, the member may appear in constant expressions. —end note ] The member shall still be defined
in a namespace scope if it is odr-used (3.2) in the program and the namespace scope definition shall not
contain an initializer.
It requires a definition if it is odr-used. Is it odr-used? No, it is not. The original C++11 wording in section 3.2 [basic.def.odr] says:
An expression is potentially evaluated unless it is an unevaluated operand (Clause 5) or a subexpression
thereof. A variable whose name appears as a potentially-evaluated expression is odr-used unless it is an
object that satisfies the requirements for appearing in a constant expression (5.19) and the lvalue-to-rvalue
conversion (4.1) is immediately applied.
a satisfies both conditions, it is a constant expression and the lvalue-to-rvalue conversion is immediately applied. Defect Report 712 has changed the wording which applies to C++11 since it is a defect report and 3.2 now says:
A variable x whose name appears as a potentially-evaluated expression ex is odr-used unless applying the
lvalue-to-rvalue conversion (4.1) to x yields a constant expression (5.19) 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
The potential result that matches would be:
If e is an id-expression (5.1.1), the set contains only e.
it is a constant expression and the lvalue-to-rvalue conversion is applied so it is not odr-used.

Lambda capturing constexpr object

GCC 4.7.2 compiles this:
constexpr int i = 5;
[]{ std::integral_constant< int, i >(); }; // nonstandard: i not captured
but not this:
constexpr int i = 5;
[&i]{ std::integral_constant< int, i >(); }; // GCC says i not constexpr
The latter example appears correct to me, according to C++11 §5.1.2/15:
An entity is captured by reference if it is implicitly or explicitly captured but not captured by copy. It is unspecified whether additional unnamed non-static data members are declared in the closure type for entities captured by reference.
It seems the captured object i inside the lambda refers to the variable in the enclosing scope, which is constexpr, not merely a const reference.
The standard explicitly says that the use of a by-value capture is transformed into a use of the corresponding member of the lambda object. And I think that 5.1.2 hints that my interpretation is correct.
Is there anything that explicitly says that whether a capture by reference refers to the object in the enclosing scope or a reference?
The second template-argument to std::integral_constant< int, i > is for a template-parameter of non-type form, specifically of integral or enumeration type (14.3.2p1 bullet 1) and so must be a converted constant expression of type int.
In a lambda-expression, implicit capture occurs when an entity is odr-used in the compound statement (5.1.2p11); use of a converted constant expression in an explicit template instantiation is not odr-use (3.2p3), so the first example is valid.
In the second example, I think gcc is incorrect to reject it; 5.1.2p17 says in a note that:
An id-expression that is not an odr-use refers to the original entity, never to a member of the closure type.
Although the paragraph as a whole is discussing capture by copy, there's no reason not to apply this rule to capture by reference as well. It's unsurprising that the standard is unclear on this; there's really no reason to capture an entity that can be used in a converted constant expression by reference.
First, I can confirm your observation with gcc 4.6.3 and clang 3.0 on Ubuntu 12.04.
I don't have the C++11 standard (only draft), so I cannot comment on that. But look at the, from my understanding, equivalent statements
constexpr int i = 5;
const int &j = i;
std::integral_constant<int, j>();
Neither gcc, nor clang compiles this, because j is not an "integral constant".