c++ - GCC 和 Clang 不同意 C++17 constexpr lambda 捕获

标签 c++ lambda c++17

考虑这个例子,它将一个变量声明为 constexpr,通过在 lambda 中复制来捕获它,并声明另一个 constexpr 变量,该变量是 constexpr 函数从原始变量中解包非类型模板参数的结果。

#include <utility>

template<int I>
constexpr auto unwrap(std::integral_constant<int, I>) {
  return I;
}

int main() {
  constexpr auto i = std::integral_constant<int, 42>{};
  constexpr auto l = [i]() {
    constexpr int x = unwrap(i);
  };
}

Clang(主干)接受此代码。 (wandbox)

GCC (trunk) 失败并显示以下错误消息 (wandbox):

lambda_capture.cpp:11:31: error: the value of ‘i’ is not usable in a constant expression
     constexpr int x = unwrap(i);
                               ^
lambda_capture.cpp:10:28: note: ‘i’ was not declared ‘constexpr’
   constexpr auto l = [i]() {

哪个编译器是正确的?在我看来,这是一个 GCC 错误,其中 lambda 捕获的 constexpr-ness 未正确传播到 lambda 上下文。

最佳答案

两种实现都存在错误,但我倾向于认为 GCC 在这里得到了正确的答案。


放弃对 i 的捕获导致 Clang 拒绝编译代码。这意味着它显然在某个地方存在错误。

[expr.const]/2.12 :

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;
  • [...]

Clang 的行为是精神 split 症:如果使用 i在正文中不是 odr 使用,那么它不需要被捕获,但是如果显式捕获被删除,它会拒绝 OP 中的代码; OTOH,如果是 odr-use,则按上述 unwrap(i)不是常量表达式,因此它应该拒绝 x 的初始化.


GCC 的 lambda 实现在 odr 使用方面非常糟糕。它会在超早期进行不断折叠,从而导致各种微妙的恶作剧。另一方面,对于显式捕获,它会转换所有用途,无论它是否实际上是 odr 用途。积极的常量折叠意味着如果捕获 i,它会接受 OP 的代码。被删除。

假设 unwrap(i)是否使用 ODR i ,那么根据 [expr.const]/2.12,OP 的代码格式错误是正确的。


是否 unwrap(i)实际使用 odr i ?那个问题boils down是否复制初始化unwrap的参数对象算作对 i 应用左值到右值的转换.我在标准中没有看到任何明确说明此处应用左值到右值转换的内容,而是 [dcl.init]/17.6.2表示我们调用了一个构造函数(在本例中,是简单的隐式定义的复制构造函数)传递 i作为绑定(bind)到其参数的参数,引用绑定(bind)是 odr-use 的经典示例。

可以肯定的是,应用从左到右的转换会导致复制初始化 integral_constant<int, 42>来自 i 的对象,但这里的问题是,标准中没有任何内容相反 - integral_constant<int, 42> 的所有复制初始化来自 i 的对象计为从左到右的转化。

关于c++ - GCC 和 Clang 不同意 C++17 constexpr lambda 捕获,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44386415/

相关文章:

c++ - cin.get 在 while 循环中使用逻辑运算符

c++ - 函数指针到成员函数的映射

c++ - 我的 C++ 程序在代码块中给出了正确的结果,但在 Visual Basic 2005 速成版中给出了错误的结果

c++ - C2248 : cannot access private member declared in Singleton class如何解决

java - 将 lambda 表达式替换为 IntelliJ IDEA 中的等效方法

c++ - 编写 lambda 函数 vector 时出现段错误

c++ - C++11 是否优化了 lambda 中的尾递归调用?

c++ - 反转模板(整数)参数的顺序

c++使用临时对象右值初始化左值引用以及自动推导

c++ - 虚函数覆盖和隐藏