2016-09-27 4 views
3

я могу легко это сделать:лямбда-вызов другой внешний лямбда

auto f = []()->int { return 4; }; 
auto g = [f]()->int { return f(); }); 
int i = g(); 

Тем не менее, я не могу это сделать:

int (*f)() = []()->int { return 4; }; 
int (*g)() = [f]()->int { return f(); }); 
int i = g(); 

Почему я получил такое сообщение в MSVC?

error C2440: 'initializing' : cannot convert from 'ClassName::functionName::< lambda_b2eebcdf2b88a20d8b40b0a03c412089>' to 'int (__cdecl *)(void)' 

Это происходит на линии:

int (*g)() = [f]()->int { return f(); }); 

Как сделать это правильно?

+0

Если это область видимости пространства имен, вы можете просто опустить 'f' из списка захвата. –

ответ

10
int (*f)() = []()->int { return 4; }; 

все еще хорошо, потому что lambdas с пустыми списками захвата неявно конвертируется в соответствующие указатели функций.

Это (важно) условие, однако, не встретились во второй строке:

int (*g)() = [f]()->int { return f(); }); 
      ^

Таким образом, преобразование не выполняется.

Если вы хотите хранить лямбду, которая захватывает что-то, вам либо нужен std::function, либо вывести тип с auto, как вы это делали раньше; независимо от вашего удобства. Указатели функций просто не могут этого сделать (в C++ 11, на будущее см. Yakk's answer).

3

Ну, вы можете подождать C++ 17.

template<auto F> 
struct function_ptr; 
template<class R, class...Args, R(*F)(Args...)> 
struct function_ptr<F> { 
    using signature = R(Args...); 
    constexpr R operator()(Args...args)const { 
    return F(std::forward<Args>(args)...); 
    } 
    constexpr operator signature*() const { return F; } 
    constexpr signature* operator+() const { return F; } 
}; 

Сейчас:

constexpr auto f_ = []()->int { return 4; }; 
function_ptr<+f_> f; 

генерирует функцию-указатель, как f.

template<class T>struct tag_t {}; 

template<class F, class...Fs, class R, class...Args> 
constexpr auto chain_functions(tag_t<R(Args...)>) { 
    constexpr r = [](Args...args)->R{ 
    return F{}(Fs{}..., std::forward<Args>(args)...); 
    }; 
    return function_ptr<+r>{}; 
} 

позволяет нам цепочки функций указателей.

constexpr auto f_ = []()->int { return 4; }; 
function_ptr<+f_> f0; 
constexpr auto g_ = [](int(*f)())->int { return f(); }); 
function_ptr<+g_> g_raw; 
auto g0 = chain_functions< function_ptr<+g_>, function_ptr<+f_> >(tag_t<int()>{}); 

Теперь g является function_ptr.

int(*g)() = g0; 

следует надеяться скомпилировать и работать. (Не тестировалось, у меня нет доступа к достаточно компилятору C++ 17).

По-прежнему немного тупой, и определенно не проверен. В основном function_ptr предназначен для создания типа, который содержит указатель функции времени компиляции. C++ 17 предоставляет нам constexpr lambdas, включая возможность вывести из них указатель функции в контексте constexpr.

Затем мы можем скомпоновать эти типы указателей функций для генерации нового типа указателя функции.