【问题标题】:Initializing capturing lambda in ternary operator在三元运算符中初始化捕获 lambda
【发布时间】: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; };

【问题讨论】:

    标签: c++ c++11 lambda c++14 language-lawyer


    【解决方案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);
    

    【讨论】:

      猜你喜欢
      • 2023-01-02
      • 2013-07-27
      • 2015-07-11
      • 2021-07-03
      • 2010-12-20
      • 2012-07-14
      • 2013-03-30
      • 1970-01-01
      • 2016-07-27
      相关资源
      最近更新 更多