在Clang和GCC中移动可赋值的lambda函数

3

我有这个程序:

int main()
{
  auto l([](){});

  ::std::cout << ::std::is_move_assignable<decltype(l)>{} << ::std::endl;
}

gcc-6.1.1 显示 0

clang-3.8.0 显示 1

这导致我的程序编译时出现错误。哪个编译器是正确的?

错误信息:

error: object of type '(lambda at t.cpp:5:5)' cannot be assigned because its copy assignment operator is implicitly deleted

但这与我的问题无关。

展示你所获得的错误。 - Jesper Juhl
2个回答

4
N4140(大约是C++14)说:
5.1.2 Lambda表达式[expr.prim.lambda]
20 与lambda表达式相关联的闭包类型具有已删除的(8.4.3)默认构造函数和已删除的复制赋值运算符。它具有一个隐式声明的复制构造函数(12.8),并且可能具有一个隐式声明的移动构造函数(12.8)。[注意:复制/移动构造函数的隐式定义方式与任何其他隐式声明的复制/移动构造函数的隐式定义方式相同。-- end note]
请注意,这并未提及是否隐式声明了已删除的复制赋值运算符。编译器将lambda转换为类定义和实例化,但该类可以以巧妙的方式定义,其中复制赋值运算符被隐式声明,但该类的某些其他属性导致该隐式复制赋值运算符被删除。
然后:

12.8 复制和移动类对象[class.copy]

20 如果类 X 的定义没有显式声明移动赋值运算符,那么只有在以下条件下才会隐式声明为默认的:

(20.1) -- X 没有用户声明的复制构造函数,

(20.2) -- X 没有用户声明的移动构造函数,

(20.3) -- X 没有用户声明的复制赋值运算符,并且

(20.4) -- X 没有用户声明的析构函数。

如果 lambda 函数的复制赋值运算符被隐式声明,则不会抑制移动赋值运算符的生成。如果它被显式声明,则移动赋值运算符将被禁止。

这两种行为都可以根据标准的字面措辞得到证明。

这部分问题已经在 CWG issue 1891 中得到部分解决,其中修改了文本如下:

lambda表达式相关的闭包类型没有默认构造函数和删除的复制赋值运算符。它有一个默认的复制构造函数和一个默认的移动构造函数(12.8 [class.copy])。[注意:这些特殊成员函数通常会被隐式定义,并因此可能被定义为已删除。-- 结束说明]
然而,尽管移动赋值运算符在该问题中被提出为一个问题,但它并不改变答案,它仍然保持开放的可能性。

-1
一个带有空捕获列表的lambda函数被定义为可赋值给函数指针类型,因此如果您的实际代码也有这种类型的lambda函数,您可以简单地使用函数指针。

1
随意在示例中捕获一些内容。 - user1095108
@user1095108 嗯,你的问题展示了一个没有捕获的lambda,并问它是否应该是可移动赋值的。你需要为这个问题准备一个最小可复现示例(在这种情况下,只需有一个被捕获的变量即可)。 - Javier Martín

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接