c ++ 14用于函数绑定的Variadic lambda捕获

时间:2015-12-15 23:37:05

标签: c++ lambda c++14 variadic-templates variadic-functions

我正在阅读一些书籍以了解c ++ 14的功能。我试图使用可变参数模板将参数绑定到函数。我知道如何使用std :: bind执行此操作,但我还想使用c ++ 14 lambda表达式实现此函数,仅用于常识和理解,以及任何可能的性能优势。我已经读过lambdas可以内联而std :: bind不能内联,因为它是通过调用函数指针来实现的。

以下是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 :: forward':2个重载中没有一个可以转换所有参数类型

错误C2198'int(__ cdecl&amp;)(int,int)':调用的参数太少

我认为当可变参数转发到lambda时可能会发生错误,但我不太确定。

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

2 个答案:

答案 0 :(得分:5)

  

我已经读过可以内联lambdas而std :: bind不能内联   因为它是通过调用函数指针来实现的。

如果你将simpleAdd传递给然后绑定参数的东西,那么你是否使用bind并不重要。你觉得lambda用func捕获了什么?它是一个函数指针。

lambda-vs-function-pointer案例是关于写bind(simpleAdd, 2, 3)[] { return simpleAdd(2, 3); }的关系。或直接绑定一个lambda,如[](auto&&...args) -> decltype(auto) { return simpleAdd(decltype(args)(args)...); }与绑定simpleAdd(将使用函数指针)。

无论如何,实施它是非常棘手的。你不能使用引用捕获,因为事情很容易晃来晃去,你不能使用简单的按值捕获,因为即使对于rvalues也会复制参数,你不能做初始捕获中的包扩展。

这遵循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,例如bind,因为make_tuple会这样做。

原始代码出现故障,因为lambda的函数调用运算符argsconst(默认情况下为const),forward结束试图抛弃常数。

答案 1 :(得分:1)

你使用元组:

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

apply defined 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