Common lambda and its argument as a constant expression

The following code is adopted by GCC 7.2 and clang 5.0.0, but is rejected by Microsoft VS 2017 15.5.0 Preview 5 and the Intel C ++ 19 compiler:

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 we replace f(x) with f(decltype(x){}) , then both Microsoft and Intel will not complain. I understand that x not a constant expression, but not used inside f . This is probably why GCC and clang are not complaining.

I think the Microsoft and Intel compilers are correct in abandoning this code. What do you think?

+7
c ++ language-lawyer lambda c ++ 14
source share
2 answers

From [expr.const] :

The expression e is an expression of the basic constant if the estimate e, following the rules of an abstract machine, does not evaluate one of the following expressions:

  • [...]
  • lvalue-rvalue conversion if it does not apply to

    • a non-volatile glvalue of an integral or enumerated type that refers to a complete non-volatile const object with previous initialization, initialized with a constant expression, or
    • unstable glvalue value that refers to a subobject of a string literal or
    • an immutable value of glvalue that refers to a non-volatile object defined using constexpr, or which refers to an immutable subobject of such an object, or
    • nonvolatile glvalue of type literal, which refers to a non-volatile object whose lifetime began with an estimate of e;
  • [...]

In f(x) we do the conversion of lvalue-to-rvalue to x . x not an integral or enumerated type, it is not a subobject of a string literal, it is not an object defined using constexpr, and its lifetime does not start with an estimate of f(x) .

It looks like this is not a basic constant expression.

However , as Casey points out, since S empty, nothing in its implicitly generated copy constructor actually calls this lvalue-to-rvalue conversion. This would mean that nothing in this expression actually violates any constraints of the constraint of the main constant, and therefore gcc and clang are correct in accepting this. This interpretation seems to me true. constexpr is fun.

+4
source share

This is not a gcc / clang error. The same behavior can be reproduced in C ++ 11 using the template function:

 template <typename T> void foo(T x) { constexpr int e = f(x); } int main() { foo(S{}); } 

at godbolt.org


The question is ...

 template <typename T> void foo(T x) { constexpr int e = f(x); } 

... is f(x) constant expression?

From [expr.const] :

The expression e is an expression of the basic constant if the estimate e , following the rules of an abstract machine, does not evaluate one of the following expressions:

  • calling a function other than the constexpr constructor for a literal class, a constexpr function constexpr or an implicit trivial destructor call

S{} and 0 are constant expressions because they do not violate any of the rules in [expr.const]. f(x) is a constant expression because it is a call to the constexpr function.

If I'm missing something, gcc and clang are true here.

+2
source share

All Articles