Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 672
Vittorio Romeo Avatar asked Dec 30 '18 13:12

Vittorio Romeo


1 Answers

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.

like image 135
xskxzr Avatar answered Oct 06 '22 04:10

xskxzr