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);
};
}
其中 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
, 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_wrapper
s,因为 make_tuple
会这样做。
你的原始代码崩溃了,因为 args
在 lambda 的函数调用运算符中是 const
(默认情况下是 const
),并且 forward
最终尝试抛弃常数。
我目前正在阅读几本书以了解 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);
};
}
其中 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
, 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_wrapper
s,因为 make_tuple
会这样做。
你的原始代码崩溃了,因为 args
在 lambda 的函数调用运算符中是 const
(默认情况下是 const
),并且 forward
最终尝试抛弃常数。