Câu trả lời:
Trình biên dịch riêng lẻ được dịch sang các lớp khác nhau bởi trình biên dịch. Ví dụ: định nghĩa của lambda1 tương đương với:
class SomeCompilerGeneratedTypeName {
public:
SomeCompilerGeneratedTypeName(...) { // Capture all the required variables here
}
void operator()(T& arg) const {
// ...
}
private:
// All the captured variables here ...
};
Do đó, hai loại khác nhau được tạo bởi trình biên dịch, điều này gây ra sự không tương thích về kiểu cho auto lambda = condition ? lambda1 : lambda2;
Sau đây sẽ làm việc:
auto lambda = condition ? std::function<void(T&)>(lambda1) : std::function<void(T&)>(lambda2);
Để làm nổi bật rằng cả hai lambdas thực sự là các loại khác nhau, chúng ta có thể sử dụng <typeinfo>
từ thư viện tiêu chuẩn và typeid
toán tử. Lambdas không phải là loại đa hình, do đó, tiêu chuẩn đảm bảo rằng toán tử 'typeid' được đánh giá tại thời điểm biên dịch. Điều này cho thấy ví dụ sau là hợp lệ ngay cả khi RTTI bị tắt:
#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;
}
Đầu ra của chương trình là (với GCC 8.3, xem trên Gobolt ):
Z4mainEUlRZ4mainE1TE_/7654536205164302515
Z4mainEUlRZ4mainE1TE0_/10614161759544824066
SomeCompilerGeneratedTypeName1
vàSomeCompilerGeneratedTypeName2
Thật kỳ lạ, nếu lambdas không bị bắt, +
có thể sử dụng thủ thuật toán tử :
auto lambda1 = [](int arg) { ... };
auto lambda2 = [](int arg) { ... };
auto lambda = condition ? +lambda1 : +lambda2; // This compiles!
lambda(2019);
Điều này hoạt động, bởi vì +
sẽ chuyển lambda thành một con trỏ hàm và cả hai con trỏ hàm có cùng loại (giống như void (*)(int)
).
Với GCC và Clang (nhưng không phải với MSVC), +
có thể được bỏ qua, lambdas vẫn sẽ được chuyển đổi thành con trỏ hàm.
Trình biên dịch không thể quyết định loại nào auto
sẽ là:
auto lambda = condition ? lambda1 : lambda2;
vì mỗi lambda có một loại khác nhau và độc đáo.
Một cách sẽ làm việc là:
auto lambda = [&](T& arg) {
return (condition ? lambda1(arg) : lambda2(arg));
}
Nó không biên dịch vì mỗi lambda có một loại duy nhất, không có loại chung cho ?:
.
Bạn có thể gói chúng vào std::function<void(T&)>
, vd
auto lamba1 = [&](T& arg) {
...
};
auto lambda2 = [&](T& arg) {
...
};
auto lambda = condition ? std::function(lambda1) : lambda2; // C++17 class template deduction