移动clang和gcc中可分配的lambda

move assignable lambdas in clang and gcc

本文关键字:可分配 lambda gcc clang 移动      更新时间:2023-10-16

我有这个程序:

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

但这与我的问题无关。

N4140(大致为C++14)表示:

5.1.2 Lambda表达式[expr.prim.Lambda]

20与lambda表达式关联的闭包类型有一个已删除的(8.4.3)默认构造函数和一个已被删除的复制赋值运算符。它有一个隐式声明的复制构造函数(12.8),也可能有一个显式声明的移动构造函数(12.8)。[注意:复制/移动构造函数的隐式定义方式与隐式定义任何其他隐式声明复制/移动构造器的方式相同。--结束注释]

请注意,这并没有提到是否隐式声明了已删除的副本分配运算符。编译器将lambda转换为类定义和实例化,但可以巧妙地定义该类,即隐式声明复制赋值运算符,但该类的某些其他属性会导致该隐式复制赋值运算符被删除。

然后:

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

20如果类X的定义没有显式声明移动赋值运算符,则当且仅当

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

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

(20.3)--X没有用户声明的副本分配运算符,并且

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

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

根据标准的字面措辞,这两种行为都是可以辩护的。

1891年的CWG问题部分解决了这一问题,将文本改为:

lambda表达式关联的闭包类型没有默认构造函数和已删除的复制赋值运算符。它有一个默认复制构造函数和一个默认移动构造函数(12.8[class.copy])。[注意:这些特殊成员函数是像往常一样隐式定义的,因此可能被定义为已删除。--end Note]

然而,尽管移动分配操作员被认为是该问题的一个问题,但它并没有改变答案,它仍然保留着可能性。

具有空捕获列表的lambda被定义为可分配给函数指针类型,因此如果实际代码也具有该类型的lambda函数,则可以使用函数指针。