Initializing capturing lambda in ternary operator

2020-02-23 06:32发布

I wanted to create a lambda in the following way:

auto l1 = condition ?
          [](){ return true; } :
          [number](){ return number == 123; };

However, I got error:

operands to ?: have different types ‘main()::<lambda()>’ and ‘main()::<lambda()>’

Obviously, the types seem to be the same. I thought, that capturing number in only one of lambdas might be a problem, but I get the same error for these:

//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; };

I'm aware I can do it other way (below), but I'm wondering what is the cause of this behavior. It was compiled with GCC6.3.1, C++14 enabled.

//compiles      
auto l4 = condition ?
          [](const int){ return true; } :
          [](const int number){ return number == 123; };

3条回答
欢心
2楼-- · 2020-02-23 07:00

If you really want to use the conditional operator you can do it like this:

auto l1 = condition
          ? std::function<bool()>{[](){ return true; }}
          : std::function<bool()>{[number](){ return number == 123; }};

In C++17 this can be simplified thanks to Class template argument deduction:

auto l1 = condition
          ? std::function{[](){ return true; }}
          : std::function{[number](){ return number == 123; }};
查看更多
\"骚年 ilove
3楼-- · 2020-02-23 07:07

A lambda in C++ is an instance of local class implementing functor contract. I mean, operator() etc. And these classes are unrelated and have different types.

Your code is equivalent to

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);
查看更多
一夜七次
4楼-- · 2020-02-23 07:12

Every lambda expression has unique type (i.e. the closure type, which is a unique unnamed non-union non-aggregate class type), even with the same signature and function body; the compiler just can't deduce the common type of ternary conditional operator for the variable declared by auto, the two closure types are irrelevant at all.

You can use std::function instead. e.g.

std::function<bool()> l1;
if (condition)
     l1 = [](){ return true; };
else
     l1 = [number](){ return number == 123; };

For l4, note that the lambda-expression with empty capture list could be converted to the corresponding function pointer implicitly. In this case both of them could be converted to the same function pointer type (i.e. bool(*)(int)), which then could be deduced as the common type of ternary conditional operator and the type of l4.

查看更多
登录 后发表回答