Почему я не могу создать вектор лямбда-выражений (одного типа) в C ++ 11?


88

Я пытался создать вектор лямбда, но не смог:

auto ignore = [&]() { return 10; };  //1
std::vector<decltype(ignore)> v;     //2
v.push_back([&]() { return 100; });  //3

До строки №2 компилируется нормально . Но строка №3 дает ошибку компиляции :

ошибка: нет соответствующей функции для вызова 'std :: vector <main () :: <lambda () >> :: push_back (main () :: <lambda ()>)'

Мне не нужен вектор указателей на функции или вектор объектов функций. Однако вектор функциональных объектов, которые инкапсулируют реальные лямбда-выражения, подойдет мне. Это возможно?


23
«Мне не нужен вектор указателей на функции или вектор объектов функций». Но это то, о чем вы просили. Лямбда - это функциональный объект.
Никол Болас

Ответы:


135

Каждая лямбда имеет разный тип, даже если у них одна и та же сигнатура. Вы должны использовать инкапсулирующий контейнер времени выполнения, например, std::functionесли вы хотите сделать что-то подобное.

например:

std::vector<std::function<int()>> functors;
functors.push_back([&] { return 100; });
functors.push_back([&] { return  10; });

52
Управление командой разработчиков из ста человек для меня больше похоже на кошмар :)
Джереми Фриснер

10
Также не забывайте, что лямбда-выражения без захвата ([] -стиль) могут превратиться в указатели на функции. Таким образом, он мог хранить массив указателей на функции одного и того же типа. Обратите внимание, что в VC10 это еще не реализовано.
Никол Болас

Кстати, не следует ли вообще использовать в этих примерах без захвата? Или это необходимо? - Кстати, лямбда без захвата для указателя на функцию, похоже, поддерживается в VC11. Хотя не проверял.
Klaim

2
Можно ли создать вектор, хранящий функции разного типа? то есть, вместо того, чтобы ограничиваться std::function<int(), могу ли я использовать разные прототипы функций?
manatttta

2
@manatttta В чем будет смысл? Контейнеры существуют для хранения объектов одного типа, для организации и управления ими вместе. Вы также можете спросить: «Могу ли я создать vectorхранилище и std::functionи std::string?» И ответ тот же: нет, потому что это не по назначению. Вы можете использовать класс в стиле «вариант» для выполнения стирания типа, достаточного для помещения несопоставимых вещей в контейнер, в то же время включая метод, позволяющий пользователю определить «настоящий» тип и, следовательно, выбрать, что с ним делать (например, как вызвать) каждый элемент ... но опять же, зачем так далеко? Есть ли какое-нибудь реальное объяснение?
underscore_d

40

Все лямбда-выражения имеют разный тип, даже если они посимвольно идентичны . Вы вставляете лямбду другого типа (потому что это другое выражение) в вектор, и это явно не сработает.

Одно из решений - std::function<int()>вместо этого создать вектор .

auto ignore = [&]() { return 10; };
std::vector<std::function<int()>> v;
v.push_back(ignore);
v.push_back([&]() { return 100; });

С другой стороны, это не лучшая идея, [&]если вы ничего не захватываете.


14
Не нужны ()лямбды, которые не принимают аргументов.
Puppy

18

Хотя то, что другие сказали, актуально, все еще можно объявить и использовать вектор лямбда, хотя это не очень полезно:

auto lambda = [] { return 10; };
std::vector<decltype(lambda)> vec;
vec.push_back(lambda);

Таким образом, вы можете хранить там любое количество лямбд, если это копия / перемещение lambda!


Что на самом деле может быть полезно, если возврат происходит в цикле с другими параметрами. Предположительно для ленивых оценочных целей.
MaHuJa

7
Нет, вы не помещаете параметры в вектор, а только в объект функции ... Так что это будет вектор со всеми копиями одной и той же лямбды
hariseldon78

16

Если ваша лямбда не имеет состояния, т. Е. [](...){...}C ++ 11 позволяет ей преобразоваться в указатель на функцию. Теоретически компилятор, совместимый с C ++ 11, сможет это скомпилировать:

auto ignore = []() { return 10; };  //1 note misssing & in []!
std::vector<int (*)()> v;     //2
v.push_back([]() { return 100; });  //3

4
Для записи auto ignore = *[] { return 10; };сделает . ignoreint(*)()
Люк Дантон

1
@ Люк, это мерзко! Когда они это добавили?
MSN

3
Что ж, поскольку функция преобразования, которая позволяет принимать указатель на функцию в первую очередь, обязана не быть explicit, разыменование лямбда-выражения является допустимым и разыменовывает указатель, полученный в результате преобразования. Затем с помощью autoраспада этой ссылки обратно в указатель. (Используя auto&или auto&&сохранил бы ссылку.)
Люк Дантон

Ах ... Разыменование полученного указателя. Это имеет смысл. Пропуск был ()намеренным или случайным?
MSN

Умышленно лямбда-выражение эквивалентно (но на два символа короче).
Люк Дантон

6

Вы можете использовать функцию генерации лямбда (обновлено исправлением, предложенным Nawaz):

#include <vector>
#include <iostream>

int main() {
    auto lambda_gen = [] (int i) {return [i](int x){ return i*x;};} ;

    using my_lambda = decltype(lambda_gen(1));

    std::vector<my_lambda> vec;

    for(int i = 0; i < 10; i++) vec.push_back(lambda_gen(i));

    int i = 0;

    for (auto& lambda : vec){
        std::cout << lambda(i) << std::endl;
        i++;
    }
}

Но я думаю, что на этом этапе вы в основном создали свой собственный класс. В противном случае, если лямбды имеют совершенно разные caputres / args и т. Д., Вам, вероятно, придется использовать кортеж.


Хорошая идея обернуть его в функцию, lambda_genкоторая, в свою очередь, может быть лямбдой. Однако auto a = lambda_gen(1);делает ненужный вызов, которого можно избежать, если мы напишем это decltype(lambda_gen(1)).
Nawaz

Но разве это не лишний вызов? Также еще один незначительный момент заключается в том, что в вопросе говорится о С ++ 11, поэтому, как мне кажется, нужно добавить конечный возвращаемый тип к функции.
допотопный

Нет. Все, что decltype находится внутри, не оценивается , поэтому вызов на самом деле не производится. То же самое и с sizeof. Кроме того, этот код не будет работать в C ++ 11, даже если вы добавите конечный возвращаемый тип !!
Nawaz

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.