回答:
個々のラムダは、コンパイラーによって異なるクラスに変換されます。たとえば、lambda1の定義は次と同等です。
class SomeCompilerGeneratedTypeName {
public:
SomeCompilerGeneratedTypeName(...) { // Capture all the required variables here
}
void operator()(T& arg) const {
// ...
}
private:
// All the captured variables here ...
};
したがって、2つの異なるタイプがコンパイラーによって生成され、これにより、タイプの非互換性が発生します。 auto lambda = condition ? lambda1 : lambda2;
以下はうまくいくでしょう:
auto lambda = condition ? std::function<void(T&)>(lambda1) : std::function<void(T&)>(lambda2);
両方のラムダが実際に異なる型であることを強調するために<typeinfo>
、標準ライブラリとtypeid
演算子から使用できます。ラムダはポリモーフィック型ではないため、標準では「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
SomeCompilerGeneratedTypeName1
とSomeCompilerGeneratedTypeName2
奇妙なことに、ラムダがキャプチャレスである場合、オペレーター+
トリックを使用できます。
auto lambda1 = [](int arg) { ... };
auto lambda2 = [](int arg) { ... };
auto lambda = condition ? +lambda1 : +lambda2; // This compiles!
lambda(2019);
これ+
は機能します。これは、がラムダを関数ポインタに変換し、両方の関数ポインタが同じ型(のようなものvoid (*)(int)
)を持っているためです。
GCCとClang(MSVC +
では不可)を使用すると、省略できますが、ラムダは関数ポインターに変換されます。