2010-07-01 9 views
8

В 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, возможно, вы можете вести меня в правильном направлении.

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

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

Спасибо!

Edit: я компиляция с Visual Studio 2010

ответ

14

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

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;}; 
} 

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

+0

Спасибо, +1. Что вы подразумеваете под «если вы лямбда-функция не захватывает ничего из внешней области, то вы можете использовать указатели функций»? Разве это не захват х? – Cam

+0

@incrediman Да, ваш пример захвачен, поэтому вы не можете использовать указатели на функции, только функции лямбда, которые не имеют состояния, могут быть преобразованы в указатели функций. –

+0

@snk_kid: Ох. Я на самом деле просто неправильно понял, что вы написали, это имеет смысл так, как это происходит :) – Cam

6

От Wikipedia:

Лямбда-функции являются функциональными объектами зависящей от реализации типа; имя этого типа доступно только компилятору. Если пользователь хочет использовать лямбда-функцию в качестве параметра, тип должен быть типом шаблона, или он должен создать 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 
+0

Это работает отлично, спасибо! Любая идея, что делает 'std :: function', чтобы сделать это возможным? – Cam

+0

std :: function does template magic, похожее на то, что std :: bind1st делает, кроме std :: function намного проще в использовании :-). Он основан на инфраструктуре boost :: function, которую вы можете увидеть здесь http://www.boost.org/doc/libs/1_43_0/doc/html/function/tutorial.html#id866455. В конечном итоге std :: function создает новый класс (используя шаблоны). Этот новый класс использует неявное литье, чтобы включить лямбда-функцию в новый класс. – Dragontamer5788