lambda的C++三元赋值

C++ ternary assignment of lambda

知道为什么以下代码段无法编译吗?它抱怨错误 "error: operands to ?: have different types"

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

由于 2 个 lambdas(lambda1lambda2)是 2 个不同的类型,?: 无法从 lambda1 推导出 lambda 的 return 类型] 和 lambda2。发生这种情况是因为这 2 个不能相互转换。

它无法编译,因为每个 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

编译器无法决定 auto 应该是什么类型:

auto lambda = condition ? lambda1 : lambda2;

因为每个 lambda 都有不同且唯一的类型。

一种可行的方法是:

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

编译器将各个 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,see on Gobolt):

Z4mainEUlRZ4mainE1TE_/7654536205164302515
Z4mainEUlRZ4mainE1TE0_/10614161759544824066

奇怪的是,如果 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 仍将转换为函数指针。