Fed*_*dor 5 c++ lambda visual-studio language-lawyer c++20
从 C++20 开始,不带捕获的闭包类型具有默认构造函数,请参阅https://en.cppreference.com/w/cpp/language/lambda:
如果未指定捕获,则闭包类型具有默认的默认构造函数。
但是对于捕获的闭包类型来说,如何构造它们的对象呢?
一种方法是使用std::bit_cast(前提是闭包类型可以简单地复制)。Visual Studio 编译器为闭包类型提供了一个构造函数,如示例所示:
#include <bit>
int main() {
int x = 0;
using A = decltype([x](){ return x; });
// ok everywhere
constexpr A a = std::bit_cast<A>(1);
static_assert( a() == 1 );
// ok in MSVC
constexpr A b(1);
static_assert( b() == 1 );
}
Run Code Online (Sandbox Code Playgroud)
演示: https: //gcc.godbolt.org/z/dnPjWdYx1
考虑到 Clang 和 GCC 都拒绝A b(1),标准不要求存在此构造函数。但是编译器可以提供这样的构造函数作为扩展吗?
Nic*_*las 11
但是对于捕获的闭包类型来说,如何构造它们的对象呢?
你不能。它们只能从 lambda 表达式创建。
不,bit_cast并不“在任何地方都有效”。C++ 标准中没有任何规则要求任何特定的 lambda 类型必须是可简单复制的(或者与其捕获成员的大小相同)。当前的实现没有破坏您的代码这一事实并不意味着未来的实现不会。
如果你有多个捕获成员,它肯定行不通。
不要再将 lambda 视为创建类型的廉价方法了。如果您想使用可以构造的成员创建可调用类型,请执行以下操作:
#include <bit>
int main() {
struct A
{
int x = 0;
constexpr auto operator() {return x;}
};
// ok everywhere
constexpr A b(1);
static_assert( b() == 1 );
}
Run Code Online (Sandbox Code Playgroud)
小智 8
由于它被标记为language-lawyer,因此 C++ 标准对此有如下规定。
但是对于捕获的闭包类型来说,如何构造它们的对象呢?
cppreference 链接引用的标准的实际部分是[expr.prim.lambda.general] - 7.5.5.1.14:
如果 lambda 表达式具有 lambda 捕获,则与 lambda 表达式关联的闭包类型没有默认构造函数,否则具有默认的默认构造函数。它有一个默认的复制构造函数和一个默认的移动构造函数([class.copy.ctor])。如果 lambda 表达式具有 lambda 捕获,则它具有已删除的复制赋值运算符,否则具有默认的复制和移动赋值运算符 ([class.copy.assign])。
lambda 表达式的类型(也是闭包对象的类型)是唯一的、未命名的非联合类类型,称为闭包类型,其属性如下所述。
闭包类型不是聚合类型。实现可以定义与下面描述的不同的闭包类型,前提是这不会改变程序的可观察行为,除了更改:[不相关的东西]
这意味着(除了不相关的例外),所描述的 lambda 接口是详尽的。由于除了默认构造函数之外没有列出其他构造函数,因此这是唯一应该存在的构造函数。
注意: lambda 可能相当于基于类的函子,但它并不是纯粹的语法糖。编译器/实现不需要构造函数来构造和参数化 lambda 类型。只是程序员由于缺少构造函数而无法创建实例。
就扩展而言:
但是编译器可以提供这样的构造函数作为扩展吗?
是的。编译器可以提供此功能作为扩展,只要它所做的只是使格式不正确的程序发挥作用。
来自[intro.compliance.general] - 4.1.1.8:
一致的实现可以具有扩展(包括附加的库函数),只要它们不改变任何格式良好的程序的行为。根据本文档,需要实施来诊断使用此类格式不正确的扩展的程序。然而,这样做之后,他们可以编译并执行此类程序。
然而,对于手头的功能,MSVC 在作为扩展的实现中会遇到问题:
因此,无论是有意还是无意,MSVC 的表现就好像这是该语言的一部分,但据我所知,情况并非如此。
| 归档时间: |
|
| 查看次数: |
202 次 |
| 最近记录: |