Lambda的C ++三元赋值


11

知道为什么以下代码段无法编译吗?它报错“错误:?的操作数:具有不同的类型”

  auto lambda1 = [&](T& arg) {
      ...
  };
  auto lambda2 = [&](T& arg) {
      ...
  };
  auto lambda = condition ? lambda1 : lambda2;

Answers:


11

编译器将各个lambda转换为不同的类。例如,lambda1的定义等效于:

class SomeCompilerGeneratedTypeName {
public:
  SomeCompilerGeneratedTypeName(...) { // Capture all the required variables here
  }

  void operator()(T& arg) const {
    // ...
  }

private:
  // All the captured variables here ...
};

因此,编译器会生成两种不同的类型,从而导致以下类型的不兼容: auto lambda = condition ? lambda1 : lambda2;

以下将起作用:

auto lambda = condition ? std::function<void(T&)>(lambda1) : std::function<void(T&)>(lambda2);

为了强调两个lambda确实是不同的类型,我们可以<typeinfo>从标准库和typeid运算符中使用。Lambda不是多态类型,因此标准保证了'typeid'运算符在编译时进行求值。这表明即使禁用了RTTI,以下示例也有效:

#include <iostream>
#include <typeinfo>

int main()
{
    struct T {

    };

    auto lambda1 = [&](T& arg) {
        return;
    };

    auto lambda2 = [&](T& arg) {
      return;
    };

    std::cout << typeid(lambda1).name() << "/" << typeid(lambda1).hash_code() << std::endl;
    std::cout << typeid(lambda2).name() << "/" << typeid(lambda2).hash_code() << std::endl;

    return 0;
}

程序的输出为(使用GCC 8.3,请参阅Gobolt上的内容):

Z4mainEUlRZ4mainE1TE_/7654536205164302515
Z4mainEUlRZ4mainE1TE0_/10614161759544824066

完全错误是“错误:?的操作数:具有不同的类型'f(const std :: vector <int>&,size_t,size_t)[with T = unsigned char; size_t = long unsigned int] :: <lambda(unsigned char& )>'和'f(const std :: vector <int>&,size_t,size_t)[with T = unsigned char; size_t = long unsigned int] :: <lambda(unsigned char&)>'“,在其中相同的所有类型和格式。

1
@cow因为lambda本身具有相同的签名,所以编译器为了隐藏其实现细节并给出更易于理解的错误,为您提供了两个lambda相同的位置和签名。但最终,他们仍然理解为SomeCompilerGeneratedTypeName1SomeCompilerGeneratedTypeName2
Xatyrian

1
@cow我添加了一个突出显示答案开头的示例,您可能会发现它很有趣
Xatyrian

12

奇怪的是,如果lambda较少捕获,+则可以使用运算符:

auto lambda1 = [](int arg) { ... };
auto lambda2 = [](int arg) { ... };

auto lambda = condition ? +lambda1 : +lambda2; // This compiles!
lambda(2019); 

之所以+可行,是因为它将lambda转换为函数指针,并且两个函数指针都具有相同的类型(类似void (*)(int))。

使用GCC和Clang(但不能使用MSVC),+可以省略,lambda仍将转换为函数指针。


1
但是,这在Visual Studio上不起作用。它们的扩展允许lambda转换为不同的调用conversation来阻止它。
Guillaume Racicot

@GuillaumeRacicot,感谢您的来信。您能否提供一个链接,让我可以阅读更多相关信息?
EVG


2
@GuillaumeRacicot它似乎可以在最新的MSVC版本上编译。godbolt.org/z/ZQLWxy
Brian

@布莱恩哦!这是好消息。现在,我必须更改一些代码。谢谢!
Guillaume Racicot

10

编译器无法确定auto应为哪种类型:

auto lambda = condition ? lambda1 : lambda2;

因为每个lambda都有不同且独特的类型。

一种有效的方法是:

auto lambda = [&](T& arg) {
     return (condition ? lambda1(arg) : lambda2(arg));
}

8

它不会编译,因为每个lambda都有唯一的类型,而没有通用的类型?:

您可以将它们包装在中std::function<void(T&)>,例如

auto lamba1 = [&](T& arg) {
  ...
};
auto lambda2 = [&](T& arg) {
  ...
};
auto lambda = condition ? std::function(lambda1) : lambda2; // C++17 class template deduction

8

由于2 lambdas(lambda1lambda2)是2种不同的类型,?:因此无法推断lambdafrom lambda1和的返回类型lambda2。发生这种情况是因为这两个不能相互转换。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.