5

This question Access to constexpr variable inside lambda expression without capturing answered why the ref-capture in the below example isn't strictly neccessary. But on the other hand one gets an error, if it is captured.

The error seems to be triggered by the recursive nature of foo().

template<typename T> constexpr int bar(const T& x) { // NOK //constexpr int bar(T x) { // OK return x; } template<typename T> int foo(const T& l) { constexpr auto x = l() - 1; auto y = [&]{return bar(x);}; // if ref-capture is used, the above bar(const T&) is NOK, why? if constexpr(x <= 0) { return 42; } else { return foo(y); } } auto l2 = []{ return 3; }; int main() { foo(l2); } 
5
  • It actually happens if you put anything in the capture-group ([x], [&x], [&], ...), and it is related to y not being constexpr anymore at some point, so l() - 1 is not a compile-time expression and the compilation fails. Commented May 9, 2018 at 7:45
  • With clang, this gives an error either way, which makes more sense to me. I don't see why GCC considers l() - 1 a constant expression in this context. Commented May 9, 2018 at 7:47
  • 1
    That sounds strange, since l() - 1 should be really constexpr. Commented May 9, 2018 at 7:47
  • I think clang++ is wrong here. You can test, that l() - 1 gives a constexpr with the following line in main(): constexpr auto z = l2() - 1; Commented May 9, 2018 at 8:15
  • 1
    @wimalopaan When foo<decltype(l2)> is instantiated, the parameter l is a reference that has not been initialised with a constant expression. References that have not been initialised with a constant expression cannot be used in constant expressions. Commented May 9, 2018 at 8:27

1 Answer 1

6

If we use clang as a compiler, which is usually more relevant than gcc when it comes to language lawyer, we find out that a simplified example is very telling:

template<typename T> int foo(T/*&*/ l) { constexpr auto x = l() - 1; if constexpr(x <= 0) { return 42; } else { return 0; } } auto l2 = []{ return 3; }; int main() { foo(l2); } 

Adding and removing reference in foo() signature makes program compiled or non-compiled. I believe, this has to do with bullet 12 on constant expression topic on cppreference :

an id-expression referring to a variable or a data member of reference type, unless it was initialized with a constant expression or its lifetime began within the evaluation of this expression

https://en.cppreference.com/w/cpp/language/constant_expression

So both of those statements seem to be not satisfied, as reference was not initialized with constant expression, and it's lifetime didn't begin with evaluation of expression.

Sign up to request clarification or add additional context in comments.

1 Comment

You can boil it down to the following: constexpr auto z = 1; auto y = [&]{return bar(z);}; constexpr auto z2 = y(); With the ref-capture this does not compile. And this is the reason, as @SergeyA stated: z is ref-initialized from outside the lambda.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.