在三元运算符中初始化捕获 lambda
Posted
技术标签:
【中文标题】在三元运算符中初始化捕获 lambda【英文标题】:Initializing capturing lambda in ternary operator 【发布时间】:2018-07-02 00:17:03 【问题描述】:我想通过以下方式创建一个 lambda:
auto l1 = condition ?
[]() return true; :
[number]() return number == 123; ;
但是,我得到了错误:
operands to ?: have different types ‘main()::<lambda()>’ and ‘main()::<lambda()>’
显然,类型似乎相同。我想,仅在一个 lambda 表达式中捕获 number
可能是个问题,但我得到了同样的错误:
//check if capturing number in both lambdas would help
auto l2 = condition ?
[number]() return true; :
[number]() return number == 123; ;
//maybe the first lambda capture was optimised out? let's make sure:
auto l3 = condition ?
[number]() return number != 123; :
[number]() return number == 123; ;
我知道我可以通过其他方式(如下)做到这一点,但我想知道这种行为的原因是什么。它使用 GCC6.3.1 编译,启用了 C++14。
//compiles
auto l4 = condition ?
[](const int) return true; :
[](const int number) return number == 123; ;
【问题讨论】:
【参考方案1】:每个lambda expression 都有唯一的类型(即闭包类型,它是唯一的未命名非联合非聚合类类型),即使具有相同的签名和函数体;编译器只是无法为auto
声明的变量推导出ternary conditional operator的公共类型,这两种闭包类型根本不相关。
您可以改用std::function
。例如
std::function<bool()> l1;
if (condition)
l1 = []() return true; ;
else
l1 = [number]() return number == 123; ;
对于l4
,请注意捕获列表为空的 lambda 表达式可以隐式转换为相应的函数指针。在这种情况下,它们都可以转换为相同的函数指针类型(即bool(*)(int)
),然后可以推导出为三元条件运算符的公共类型和l4
的类型。
【讨论】:
那为什么 l4 会编译呢? @knopers8 因为非捕获 lambda 隐式转换为它们的通用函数指针类型(例如 bool(*)())... 因为它们具有相同的签名。仅仅因为第一个选项中的参数没有命名并不意味着它不同。 @MarošBeťko 2 个具有相同签名的 lambda,即使 2 个具有相同主体的 lambda 也具有不同的类型。它的工作原理由 Massimiliano 指定 @knopers8 所有的 cmets 都是正确的。解释也添加到答案中。【参考方案2】:如果你真的想使用条件运算符,你可以这样做:
auto l1 = condition
? std::function<bool()>[]() return true;
: std::function<bool()>[number]() return number == 123; ;
在 C++17 中,这可以通过 Class template argument deduction 来简化:
auto l1 = condition
? std::function[]() return true;
: std::function[number]() return number == 123; ;
【讨论】:
与 lambdas 相比,不使用std::function
会带来一些开销吗?
@Yola 是的。这就是类型擦除的代价。
@bolov 什么是类型擦除
@Kapil This 可能会帮助你。【参考方案3】:
C++ 中的 lambda 是实现函子协定的本地类的实例。我的意思是,operator() 等。这些类是不相关的,并且有不同的类型。
你的代码相当于
struct l1
bool operator () const
return true;
;
struct l2
private int number_:
l2(int number): number_(number)
bool operator () const
return number_ == 123;
;
int number = ???
auto l3 = condition ? l1 : l2(number);
【讨论】:
以上是关于在三元运算符中初始化捕获 lambda的主要内容,如果未能解决你的问题,请参考以下文章