Чому я не можу створити вектор лямбда (того самого типу) в 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
"Я не хочу вектор вказівників на функції або вектор об'єктів функції." Але це те, про що ви просили. Лямбда - це об’єкт функції.
Nicol Bolas,

Відповіді:


135

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

наприклад:

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

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

10
Крім того, не забувайте, що лямбди без захоплення ([] -стиль) можуть перетворитися на покажчики функцій. Тож він міг зберігати масив покажчиків на функції того самого типу. Зауважте, що VC10 цього ще не реалізує.
Nicol Bolas

До речі, чи не слід у таких прикладах все-таки використовувати беззахоплення? Або це потрібно? - До речі, безхватний лямбда-функціональний покажчик, схоже, підтримується у 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
Не потрібні ()лямбди, які не беруть аргументів.
Щеня

18

Хоча те, що сказали інші, є актуальним, все одно можна оголосити і використовувати вектор лямбда, хоча це не дуже корисно:

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

Отже, ви можете зберігати туди будь-яку кількість лямбд, якщо це копія / ход lambda!


Що насправді може бути корисним, якщо відкат відбувається в циклі з різними параметрами. Імовірно для цілей лінивого оцінювання.
MaHuJa

7
Ні, ви не вводите параметри у вектор, просто об'єкт функції .. Отже, це буде вектор із усіма копіями тієї самої
лямбди

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; };зробили ignoreб int(*)().
Luc Danton

1
@Luc, о, це грубо! Коли вони це додали?
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 / аргументи тощо, вам, мабуть, доведеться використовувати кортеж.


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

Однак це все одно не робить додаткового дзвінка? Також ще одним другорядним моментом є те, що у питанні вказано C ++ 11, тому потрібно додати кінцевий тип повернення до функції, на мою думку.
допотопний

Ні. Все, що decltype знаходиться всередині, не оцінено , тому дзвінок фактично не здійснюється. Той самий випадок і з sizeof. Крім того, цей код не працюватиме в C ++ 11, навіть якщо ви додасте кінцевий тип повернення !!
Наваз

4

Кожна лямбда - це інший тип. Ви повинні використовувати std::tupleзамість std::vector.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.