8

Consider the following code:

template <int N, typename T> void f(T) { }

template <typename T> 
constexpr int k(T&) { return 0; }

int main() 
{
    constexpr auto i = 1;
    f<k(i)>([&i]
    {
         f<k(i)>(0); 
    });
}

clang++ (trunk) compiles it. g++ (trunk) fails with the following error:

<source>: In lambda function:

<source>:11:19: error: no matching function for call to 'f<k<const int>((* & i))>(int)'
11  |          f<k(i)>(0);
    |                   ^

<source>:1:35: note: candidate: 'template<int N, class T> void f(T)'
    1 | template <int N, typename T> void f(T) { }
      |                                   ^

<source>:1:35: note:   template argument deduction/substitution failed:

<source>:11:19: error: '__closure' is not a constant expression
11  |          f<k(i)>(0);
    |                   ^

<source>:11:13: note: in template argument for type 'int'
11  |          f<k(i)>(0);
    |            ~^~~

live example on godbolt.org


Changing k(T&) to k(T) solves the issue. It seems to me that the problem is related to the fact that the reference argument is not a constant expression, but it not used as part of k.

What compiler is correct here?

Baum mit Augen
  • 49,044
  • 25
  • 144
  • 182
Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416
  • 2
    Related: [GCC and Clang disagree about C++17 constexpr lambda captures](https://stackoverflow.com/questions/44386415/gcc-and-clang-disagree-about-c17-constexpr-lambda-captures). T.C.'s answer also answers your question. – xskxzr Dec 30 '18 at 16:16

3 Answers3

6

GCC is correct here.

According to [expr.const]/4:

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:

  • ...
  • in a lambda-expression, a reference to [...] a variable with automatic storage duration defined outside that lambda-expression, where the reference would be an odr-use; ...
  • ...

k(i) odr-uses i thus k(i) is not a constant expression in the lambda expression, so this code is ill-formed.

xskxzr
  • 12,442
  • 12
  • 37
  • 77
0

The error is emitted for the expression k(i) that appears within the lambda expression compound statements but not outside it. This is a GCC bug. According to [expr.prim.lambda.capture]/11

An id-expression within the compound-statement of a lambda-expression that is an odr-use of a reference captured by reference refers to the entity to which the captured reference is bound and not to the captured reference.

So k(i) outside the lambda is the same expression as k(i) outside the lambda, so there is no reason for GCC to emit an error for the second expression but not for the first.

Oliv
  • 17,610
  • 1
  • 29
  • 72
-1

Compiled with 0 errors on godbolt.

I used a variable result_k = k(i); for bypass this errors.

template <int N, typename T> void f(T) { }

template <typename T> constexpr int k(T&) { return 0; }

int main() {
    constexpr auto i = 1;
    const int result_k=k(i);
    f<result_k>([&i]{ f<result_k>(0);});
}
xskxzr
  • 12,442
  • 12
  • 37
  • 77
Andreadjk
  • 1
  • 1
  • 3
    This question is asking which compiler is correct, rather than how to resolve the issue. – xskxzr Dec 30 '18 at 16:26
  • clang++ (trunk) compiles it. g++ (trunk) fails with the following error: I posted the code beacause the owner use two compilers.. clang and g++, this code work with clag++ and g++ – Andreadjk Dec 30 '18 at 17:41
  • 2
    And yes, that is a answer, but not the one the OP wants to know. This is question is tagged `language-laywer` and OP asks which compiler is correct, not how to fix it. An good answer would be telling OP whether clang or gcc is right, which yours does not do. – Rakete1111 Dec 30 '18 at 20:37