【问题标题】:constexpr function with unused reference argument – gcc vs clang带有未使用引用参数的 constexpr 函数 – gcc vs clang
【发布时间】:2018-12-30 13:29:06
【问题描述】:

考虑以下代码:

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&amp;) 更改为k(T) 可解决此问题。在我看来,问题与引用参数不是常量表达式这一事实有关,但它没有用作k 的一部分。

这里什么编译器是正确的?

【问题讨论】:

标签: c++ templates language-lawyer c++17 constexpr


【解决方案1】:

GCC 在这里是正确的。

根据[expr.const]/4

表达式e 是核心常量表达式,除非求值 的e,遵循抽象机的规则,将评估 以下表达式之一:

  • ...
  • lambda-expression 中,对 [...] 一个变量的引用,该变量在 lambda-expression 之外定义了自动存储持续时间, 参考将是一种网上解决方法的使用; ...
  • ...

k(i) odr-uses i 因此k(i) 不是 lambda 表达式中的常量表达式,所以这段代码格式不正确。

【讨论】:

    【解决方案2】:

    针对出现在 lambda 表达式复合语句中但不在其外部的表达式 k(i) 发出错误。这是一个 GCC 错误。根据[expr.prim.lambda.capture]/11

    在 lambda 表达式的复合语句中的 id-expression 是对由引用捕获的引用的 odr 使用 指的是所捕获的引用绑定到的实体 而不是捕获的引用。

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

    【讨论】:

      【解决方案3】:

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

      【讨论】:

      • 这个问题是问哪个编译器是正确的,而不是如何解决这个问题。
      • clang++ (trunk) 编译它。 g++ (trunk) 失败并出现以下错误:我发布代码是因为所有者使用了两个编译器.. clang 和 g++,此代码适用于 clag++ 和 g++
      • 是的,这是一个答案,但不是 OP 想知道的答案。这是标记为language-laywer 的问题,OP 询问哪个编译器是正确的,而不是如何修复它。一个好的答案是告诉 OP clang 或 gcc 是否正确,而您的则不这样做。
      猜你喜欢
      • 2020-06-12
      • 2017-08-21
      • 1970-01-01
      • 2015-07-04
      • 2016-07-29
      • 2018-02-08
      • 1970-01-01
      • 2013-08-21
      • 2012-12-04
      相关资源
      最近更新 更多