Vittorio Romeo
Vittorio Romeo

Reputation: 93264

constexpr function with unused reference argument – gcc vs clang

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?

Upvotes: 9

Views: 518

Answers (3)

Andreadjk
Andreadjk

Reputation: 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);});
}

Upvotes: -1

xskxzr
xskxzr

Reputation: 13040

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.

Upvotes: 6

Oliv
Oliv

Reputation: 18041

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.

Upvotes: 0

Related Questions