Какой тип лямбда-функции? - PullRequest
8 голосов
/ 01 июля 2010

В C ++ 0x мне интересно, какой тип у лямбда-функции.В частности:

#include<iostream>

type1 foo(int x){
 return [x](int y)->int{return x * y;};
}

int main(){

 std::cout<<foo(3)(4);//would output 12

 type2 bar = foo(5);
 std::cout<<bar(6);//would output 30
 return 0;
}

Чем мне нужно заменить type1 / type2, чтобы вышеперечисленное заработало?Надеюсь, вы сможете увидеть, чего я пытаюсь достичь, поэтому, даже если это невозможно с помощью прямой замены type1 и type2, возможно, вы сможете направить меня в правильном направлении.

Другими словами:

  • Как я могу заставить функцию возвращать анонимную функцию?
  • Как я могу назначить анонимную функцию переменной?

Спасибо!

Редактировать: я собираю с Visual Studio 2010

Ответы [ 2 ]

14 голосов
/ 02 июля 2010

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

auto foo = [](int x, int y) { return x + y; };

Компилятор логически делает это:

struct CompilerGeneratedName { void operator()(int x, int y) const { return x + y; } };
CompilerGeneratedName foo;

Поскольку компилятор генерирует (локальный) класс, он генерирует имя, и, следовательно, вы никогда не можете явно написать тип, вы можете вывести тип только из выводов типа аргументов функции шаблона или с помощью auto / decltype.

Кроме того, замыкания C ++ 0x размещаются статически, поэтому вы все равно не можете безопасно вернуть необработанное замыкание C ++ 0x.

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

Итак, в основном вы хотите вот что:

std::function< int (int) > foo(int x)
{
    return [x](int y)->int{return x * y;};
}

Причина, по которой я продолжал говорить логически, заключается в том, что именно так изначально работает boost :: lambda (даже несмотря на то, что C ++ 03 не позволяет использовать локальные классы в аргументах функции шаблона) и в чем идея добавления Лямбда-функции берут свое начало, но поскольку это языковая функция, теперь производители компиляторов могут реализовывать ее различными и более эффективными способами, например, при захвате всей среды по ссылке компилятор может просто передать указатель на стек вызовов вместо логического способа, в то же время поддержание логического представления.

6 голосов
/ 02 июля 2010

Из Википедия :

Лямбда-функции являются объектами функций типа, зависящего от реализации; имя этого типа доступно только компилятору. Если пользователь желает принять лямбда-функцию в качестве параметра, тип должен быть типом шаблона, либо он должен создать std::function для захвата лямбда-значения.

VC10 компилирует это

//Beware, brain-compiled code ahead!
#include<iostream>
#include<functional>

std::function<int(int)> foo(int x)
{
    return [x](int y)->int{return x * y;};
}

int main(){

    std::cout<<foo(3)(4) << '\n';

    auto bar = foo(5);
    std::cout<<bar(6) << '\n';

    return 0;
}

и отпечатки

12
30
...