2015-12-15 63 views
3

我目前正在阅读几本书以抓住C++ 14的特性。我正在尝试使用可变参数模板将参数绑定到函数。我知道如何使用std :: bind来做到这一点,但我也想用C++ 14 lambda表达式来实现这个功能,只是为了常识和理解,以及任何可能的性能优势。我读过lambda表达式可以内联,而std :: bind不能内联,因为它通过调用函数指针发生。C++ 14函数绑定的可变参数lambda捕获

下面是从myFunctions.h代码:

#include <functional> 

int simpleAdd(int x, int y) { 
    return x + y; 
} 

//function signatures 
template<class Func, class... Args> 
decltype(auto) funcBind(Func&& func, Args&&...args); 

template<class Func, class... Args> 
decltype(auto) funcLambda(Func&& func, Args&&...args); 

///////////////////////////////////////////////////////////////// 

//function definitions 
template<class Func, class... Args> 
inline decltype(auto) funcBind(Func&& func, Args&&... args) 
{ 
    return bind(forward<Func>(func), forward<Args>(args)...); 
} 

template<class Func, class ...Args> 
inline decltype(auto) funcLambda(Func && func, Args && ...args) 
{ //The error is caused by the lambda below: 
    return [func, args...]() { 
     forward<Func>(func)(forward<Args>(args)...); 
    }; 
} 

这里是我运行的主要代码:

#include<iostream> 
#include<functional> 
#include "myFunctions.h" 
using namespace std; 


int main() 
{ 
    cout << "Application start" << endl; 
    cout << simpleAdd(5,7) << endl; 

    auto f1 = funcBind(simpleAdd,3, 4); 
    cout << f1() << endl; 

    //error is occurring below 
    auto f2 = funcLambda(simpleAdd, 10, -2); 
    cout << f2() << endl; 

    cout << "Application complete" << endl; 

错误C2665 '的std ::前进':没有2个重载可以转换所有的参数类型

错误C2198 'INT(__cdecl &)(INT,INT)':呼叫

参数太少

我认为错误可能发生在可变参数被转发到lambda时,但我并不确定。

我的问题是我如何正确地制定这段代码,以便我可以使用lambda捕获函数及其参数,并在稍后调用它。

回答

4

我读过lambdas可以内联,而std :: bind不能内联 ,因为它通过调用函数指针进行。

如果您将simpleAdd传递给可以绑定参数的东西,那么您是否使用bind并不重要。你认为lambda与func捕获什么?这是一个函数指针。

lambda-vs-function-pointer的情况是关于编写bind(simpleAdd, 2, 3)[] { return simpleAdd(2, 3); }。或者直接绑定类似[](auto&&...args) -> decltype(auto) { return simpleAdd(decltype(args)(args)...); }的lambda与绑定simpleAdd(它将使用函数指针)。


在任何情况下,实施它是令人惊讶的棘手。你不能使用by-reference捕获,因为事情很容易就会悬浮起来,你不能使用一个简单的按值捕获,因为它总是会拷贝参数,即使是rvalues,也不能在init中进行包扩展-捕获。

这遵循的std::bind语义(调用所述功能对象和传递所有绑定参数作为左值),除了1)它不处理的占位符或嵌套结合,和2)的函数调用操作总是const

template<class Func, class ...Args> 
inline decltype(auto) funcLambda(Func && func, Args && ...args) 
{ 
    return [func = std::forward<Func>(func), 
      args = std::make_tuple(std::forward<Args>(args)...)] { 
     return std::experimental::apply(func, args); 
    }; 
} 

cppreference具有执行std::experimental::apply

请注意,这可以解开reference_wrapper s,就像bind一样,因为make_tuple可以。

你的原代码打破了,因为args是在lambda的函数调用操作const(这是const默认情况下),以及forward结束试图抛弃常量性。

+0

谢谢你的回答。使用lambda捕获作为使用std :: bind的复杂性比我期待的要复杂得多。从cppreference.com申请和调用,但我收到一些更多的编译器错误:错误\t C2893 \t无法专门化func (_Callable &&,_ Types && ...)',以及错误\t C2975 \t'_Size':'std :: make_index_sequence'的无效模板参数,预期的编译时常量表达式。稍后我会再看一遍,但现在我会坚持使用绑定。 –

0

您可以使用一个元组:

template<class Func, class ...Args> 
inline decltype(auto) funcLambda(Func && func, Args && ...args) 
{ //The error is caused by the lambda below: 
    auto tpl = make_tuple(std::forward<Args>(args)...); 

    //Use move just in case Args has move-only types. 
    return [func, tpl = move(tpl)]() { 
     apply(func, tpl); 
    }; 
} 

哪里applydefined something like this

namespace detail { 
template <class F, class Tuple, std::size_t... I> 
constexpr decltype(auto) apply_impl(F&& f, Tuple&& t, std::index_sequence<I...>) 
{ 
    return f(std::get<I>(std::forward<Tuple>(t))...); 
} 
} // namespace detail 

template <class F, class Tuple> 
constexpr decltype(auto) apply(F&& f, Tuple&& t) 
{ 
    return detail::apply_impl(std::forward<F>(f), std::forward<Tuple>(t), 
     std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>>::value); 
} 

apply是图书馆TS版本之一的一个特征。使用C++ 17,apply_impl可以调用invoke,这将适用于任何callable

+0

谢谢你的回答。应该捕获func的lambda捕获的第一部分实际上是否说“func = forward (func)”以确保您正确地转发任何Rvalues?这应该也适用于apply_impl,而不是只是“f(...”应该说“转发(f)(...”。 –

+0

我也遇到编译器错误,当我尝试这种解决方案,其中之一类似于错误\t C2975 \t'_Size':'std :: make_index_sequence'的无效模板参数,预期的编译时常量表达式和错误\t C2198 \t'int(__cdecl * const )(int,int)':调用的参数太少 –