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?
This is not a gcc/clang bug. The same behavior can be reproduced in C++11 with a template function:
on godbolt.org
The question is, given...
...is
f(x)
a constant expression?From [expr.const]:
S{}
and0
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 aconstexpr
function.Unless I am missing something, gcc and clang are correct here.
From [expr.const]:
In
f(x)
, we do an lvalue-to-rvalue conversion onx
.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 off(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.