Отдельные лямбды переводятся компилятором в разные классы. Например, определение lambda1 эквивалентно:
classSomeCompilerGeneratedTypeName{public:SomeCompilerGeneratedTypeName(...){// Capture all the required variables here}voidoperator()(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);
Чтобы подчеркнуть, что обе лямбды действительно являются разными типами, мы можем использовать <typeinfo>стандартную библиотеку и typeidоператор. Лямбды не являются полиморфными типами, поэтому стандарт гарантирует, что оператор 'typeid' вычисляется во время компиляции. Это показывает, что следующий пример действителен, даже если RTTI отключен:
Полная ошибка: «ошибка: операнды??: Разные типы» f (const std :: vector <int> &, size_t, size_t) [с T = беззнаковый символ; size_t = длинный беззнаковый int] :: <лямбда (беззнаковый символ & )> 'и' f (const std :: vector <int> &, size_t, size_t) [с T = unsigned char; size_t = длинное unsigned int] :: <lambda (unsigned char &)> '", в котором я вижу идентичны все типы и форматы.
корова
1
@cow Поскольку лямбда сама по себе имеет одну и ту же сигнатуру, поэтому компилятор, чтобы скрыть подробности своей реализации и сделать более понятную ошибку, дает вам расположение и сигнатуру обеих лямбд, которые идентичны. Но, в конце концов, они все еще интерпретируются как SomeCompilerGeneratedTypeName1иSomeCompilerGeneratedTypeName2
Xatyrian
1
@cow Я добавил пример, который подчеркивает начало ответа, может быть, вам будет интересно
Xatyrian
12
Любопытно, что если лямбды не захватываются, +можно использовать хитрость оператора :
auto lambda1 =[](int arg){...};auto lambda2 =[](int arg){...};auto lambda = condition ?+lambda1 :+lambda2;// This compiles!
lambda(2019);
Это работает, потому +что преобразует лямбду в указатель на функцию, и оба указателя на функцию имеют одинаковый тип (что-то вроде void (*)(int)).
С GCC и Clang (но не с MSVC) +можно не указывать, лямбда-выражения будут по-прежнему преобразовываться в указатели функций.
Поскольку 2 лямбда-выражения ( lambda1и lambda2) - это два разных типа, ?:невозможно определить тип возвращаемого значения для lambdafrom lambda1и lambda2. Это происходит потому, что эти 2 не конвертируемы друг в друга.
SomeCompilerGeneratedTypeName1
иSomeCompilerGeneratedTypeName2
Любопытно, что если лямбды не захватываются,
+
можно использовать хитрость оператора :Это работает, потому
+
что преобразует лямбду в указатель на функцию, и оба указателя на функцию имеют одинаковый тип (что-то вродеvoid (*)(int)
).С GCC и Clang (но не с MSVC)
+
можно не указывать, лямбда-выражения будут по-прежнему преобразовываться в указатели функций.источник
Компилятор не может решить, какой тип
auto
должен быть:так как каждая лямбда имеет свой уникальный и уникальный тип.
Один способ, который будет работать:
источник
Он не компилируется, потому что каждая лямбда имеет уникальный тип, для которого нет общего типа
?:
.Вы можете обернуть их
std::function<void(T&)>
, например,источник
Поскольку 2 лямбда-выражения (
lambda1
иlambda2
) - это два разных типа,?:
невозможно определить тип возвращаемого значения дляlambda
fromlambda1
иlambda2
. Это происходит потому, что эти 2 не конвертируемы друг в друга.источник