Lambda闭包类型构造函数

12

cppreference 显示 lambda 闭包类型构造函数有不同的规则。

默认构造函数 - C++14之前

ClosureType() = delete; (C++14之前)

闭包类型不可默认构造。闭包类型有一个已删除(C++14之前)没有(自C++14起)默认构造函数。

默认构造函数 - 自C++14

闭包类型没有(自C++14起)默认构造函数。

默认构造函数 - 自C++20

如果未指定任何捕获变量,则闭包类型具有被默认定义的默认构造函数。否则,它没有默认构造函数(即使存在捕获默认值,即使它实际上没有捕获任何东西也是如此)。

复制赋值运算符 - C++20之前

复制赋值运算符被定义为已删除(移动赋值运算符未声明)。闭包类型不可赋值。

复制赋值运算符 - 自C++20

如果未指定任何捕获变量,则闭包类型具有被默认定义的复制赋值运算符和移动赋值运算符。否则,它有一个已删除的复制赋值运算符(即使存在捕获默认值,也是如此)。

即使没有实际捕获任何内容,当存在默认捕获时,也会出现这种情况。

规则变更的原因是什么?标准委员会是否确定了lambda闭包类型构造标准中的一些缺点?如果确定了,那么这些缺点是什么?


3
是的。C++20现在允许在未求值上下文中使用lambda表达式。http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0315r1.pdf - bolov
1
@bolov 很有趣。我得读一下这篇论文。不过,你能否简要解释一下未求值上下文如何影响lambda闭包类型的默认构造函数? - cpp_enthusiast
3
未求值的上下文并不是原因。请参考这篇论文了解背景: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0624r2.pdf - NathanOliver
@NathanOliver 那确实是正确的动机。感谢提供链接。 - bolov
1个回答

11

有一个缺陷。我们不能像人们期望的那样完全“即兴”地使用lambda表达式。C++20(允许在未评估上下文中使用lambda表达式)使得这个代码有效:

struct foo {
    int x, y;
};

std::map<foo, decltype([](foo const& a, foo const& b) { return a.x < a.y; })> m;

注意我们如何内联定义比较函数?无需创建命名的函数对象(否则可能是个好主意,但我们并不强制这样做)。也没有必要将声明分成两部分:

// C++17
auto cmp = [](foo const& a, foo const& b) { return a.x < a.y; };
std::map<foo, decltype(cmp)> m(cmp); // And also need to pass and hold it!

这样的用法(还有许多其他用法)是促使进行此更改的动因。在上面的示例中,匿名函数对象类型将带来命名函数对象类型所能带来的所有好处。其中包括默认初始化和EBO。


1
值得一提的是,C++20现在允许使用std::map<foo, decltype(cmp)> m; - NathanOliver
那么,在 lambda 闭包类型构造期间删除默认构造函数直到 C++20 是一种缺陷,对吗? - cpp_enthusiast
3
在 C++20 之前,你需要将 compare 作为参数传递给构造函数。在初始化列表后面传递它:test({ "BS", "CA", "PR", "US" }, compare);。这是正确的做法。 - StoryTeller - Unslander Monica
4
@AImx1 - 注意。Lambda表达式是一项新功能,其行为的某些方面一开始并不清晰。随着用户和编译器编写者对其经验的积累,委员会会重新评估先前被禁止的功能。如果您遵循标准化过程,您会注意到这是一个不断重复的主题。当我们不确定时最好禁用某些东西。因为它将来可能总是可以启用 :) - StoryTeller - Unslander Monica
1
@StoryTeller 这很有道理。谢谢你的解释。 - cpp_enthusiast
显示剩余4条评论

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