constexpr function with unused reference argument

2019-02-22 02:45发布

问题:

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?

回答1:

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.



回答2:

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.



回答3:

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