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

c++14 Variadic lambda capture for function binding

我目前正在阅读几本书以了解 c++14 的特性。我正在尝试使用可变参数模板将参数绑定到函数。我知道如何使用 std::bind 来执行此操作,但我也想使用 c++14 lambda 表达式来实现此功能,只是为了常识和理解,以及任何可能的性能优势。我读过 lambda 可以内联,而 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 个重载中的 none 可以转换所有参数类型

错误 C2198 'int (__cdecl &)(int,int)': 调用参数太少

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

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

您使用元组:

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, which would work for any callable.

I've read that lambdas can be inlined while std::bind cannot inline because it takes place through a call to a function pointer.

如果您将 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(这将使用函数指针)。


无论如何,实现它都非常棘手。您不能使用按引用捕获,因为事情很容易悬空,您不能使用简单的按值捕获,因为即使对于右值,它也总是会复制参数,并且您不能在 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.

请注意,这确实会像 bind 一样展开 reference_wrappers,因为 make_tuple 会这样做。

你的原始代码崩溃了,因为 args 在 lambda 的函数调用运算符中是 const(默认情况下是 const),并且 forward 最终尝试抛弃常数。