具有未使用的引用参数的 constexpr 函数——gcc vs clang

constexpr function with unused reference argument – gcc vs clang

考虑以下代码:

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) 编译它。 g++ (trunk) 失败并出现以下错误:

<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


k(T&) 更改为 k(T) 即可解决问题。在我看来,问题与参考参数不是 常量表达式 但它未用作 k.

的一部分有关

这里哪个编译器是正确的?

出现在 lambda 表达式复合语句内但不在其外的表达式 k(i) 会发出错误。这是一个 GCC 错误。根据[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.

所以 lambda 之外的 k(i) 与 lambda 之外的 k(i) 是相同的表达式,因此 GCC 没有理由为第二个表达式而不是第一个表达式发出错误。

在 godbolt 上编译时出现 0 个错误。

我使用变量 result_k = k(i); 来绕过这个错误。

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

这里 GCC 是正确的。

根据[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 因此 k(i) 不是 lambda 表达式中的常量表达式,因此此代码格式错误。