为什么 C++ 中的 lambda 从不 DefaultConstructible
Why is a lambda in C++ never DefaultConstructible
我有不捕获任何东西的 lambda,比如
[](){};
我有一个模板 class,其中包含这样一个 lambda。由于 lambda 不包含 non-static 数据成员,也不包含虚函数,因此它应该是空的 class 和 DefaultConstructible。它只是一种可用于模板元编程的策略class。我想知道,为什么这样的 class 不是 C++ 标准默认构造的。
旁注: 问了一个不同的问题,尽管标题似乎非常相似。它询问如何在没有可用默认构造函数的情况下创建无状态 lambda-object。我在问为什么没有可用的默认构造函数。
Lambda 旨在创建然后使用。因此,该标准说“不,它们没有默认构造函数”。制作一个的唯一方法是通过 lambda 表达式或相同的副本。
它们不是旨在让它们的类型成为您随身携带和使用的东西。这样做有违反 ODR 的风险,并且要求编译器避免 ODR 违规会使符号处理过于复杂。
但是,在 C++17 中,您可以围绕函数指针编写无状态包装器:
template<auto fptr>
struct function_pointer_t {
template<class...Args>
// or decltype(auto):
std::result_of_t< std::decay_t<decltype(fptr)>(Args...) >
operator()(Args&&...args)const
return fptr(std::forward<Args>(args)...);
}
};
并且由于 [](){}
上的 operator void(*)()
在 C++17 中是 constexpr
,function_pointer_t<+[](){}>
是一个什么都不做的函数对象,它是 DefaultConstructible。
这实际上并没有包装 lambda,而是 lambda 生成的指向函数的指针。
我假设您熟悉类型、对象和表达式之间的区别。在C++中,lambda特指一个lambda表达式。这是表示非平凡 object 的便捷方式。然而,这很方便:您可以通过编写代码自己创建一个类似的对象。
现在,根据 C++ 规则,每个表达式都有一个类型,但该类型不是 lambda 表达式的用途。这就是为什么它是未命名且唯一的类型的原因——C++ 委员会认为不值得定义这些属性。类似地,if 它被定义为具有默认构造函数,标准应该定义行为。使用当前规则,无需定义默认构造函数的行为。
正如您所注意到的,对于 [](){}
的特殊情况,定义默认构造函数是微不足道的。但那没有意义。您会立即遇到第一个难题:应该为什么 lambda 定义默认构造函数? lambda 的哪个子集简单到可以有一个体面的定义,又复杂到足以引起人们的兴趣?没有共识,你不能指望这个标准化。
请注意编译器供应商,作为扩展,已经可以提供这个了。标准化通常遵循现有实践,请参阅 Boost。但是,如果没有编译器供应商单独认为值得,他们为什么会一致这么认为?
对于 C++20,无状态 lambda 是默认可构造的。所以,现在这种操作是有效的:
auto func = []{};
decltype(func) another_func;
我有不捕获任何东西的 lambda,比如
[](){};
我有一个模板 class,其中包含这样一个 lambda。由于 lambda 不包含 non-static 数据成员,也不包含虚函数,因此它应该是空的 class 和 DefaultConstructible。它只是一种可用于模板元编程的策略class。我想知道,为什么这样的 class 不是 C++ 标准默认构造的。
旁注:
Lambda 旨在创建然后使用。因此,该标准说“不,它们没有默认构造函数”。制作一个的唯一方法是通过 lambda 表达式或相同的副本。
它们不是旨在让它们的类型成为您随身携带和使用的东西。这样做有违反 ODR 的风险,并且要求编译器避免 ODR 违规会使符号处理过于复杂。
但是,在 C++17 中,您可以围绕函数指针编写无状态包装器:
template<auto fptr>
struct function_pointer_t {
template<class...Args>
// or decltype(auto):
std::result_of_t< std::decay_t<decltype(fptr)>(Args...) >
operator()(Args&&...args)const
return fptr(std::forward<Args>(args)...);
}
};
并且由于 [](){}
上的 operator void(*)()
在 C++17 中是 constexpr
,function_pointer_t<+[](){}>
是一个什么都不做的函数对象,它是 DefaultConstructible。
这实际上并没有包装 lambda,而是 lambda 生成的指向函数的指针。
我假设您熟悉类型、对象和表达式之间的区别。在C++中,lambda特指一个lambda表达式。这是表示非平凡 object 的便捷方式。然而,这很方便:您可以通过编写代码自己创建一个类似的对象。
现在,根据 C++ 规则,每个表达式都有一个类型,但该类型不是 lambda 表达式的用途。这就是为什么它是未命名且唯一的类型的原因——C++ 委员会认为不值得定义这些属性。类似地,if 它被定义为具有默认构造函数,标准应该定义行为。使用当前规则,无需定义默认构造函数的行为。
正如您所注意到的,对于 [](){}
的特殊情况,定义默认构造函数是微不足道的。但那没有意义。您会立即遇到第一个难题:应该为什么 lambda 定义默认构造函数? lambda 的哪个子集简单到可以有一个体面的定义,又复杂到足以引起人们的兴趣?没有共识,你不能指望这个标准化。
请注意编译器供应商,作为扩展,已经可以提供这个了。标准化通常遵循现有实践,请参阅 Boost。但是,如果没有编译器供应商单独认为值得,他们为什么会一致这么认为?
对于 C++20,无状态 lambda 是默认可构造的。所以,现在这种操作是有效的:
auto func = []{};
decltype(func) another_func;