给定函数 f(x, y, z)
,我们可以将 x
绑定到0,获取函数 g(y, z) == f(0, y, z)
. 我们可以继续这样做并获得 h() = f(0, 1, 2)
.
在C语法中将是
#include <functional>
#include <iostream>
void foo(int a, long b, short c)
{
std::cout << a << b << c << std::endl;
}
int main()
{
std::function<void(int, long, short)> bar1 = foo;
std::function<void(long, short)> bar2 = std::bind(bar1, 0, std::placeholders::_1, std::placeholders::_2);
std::function<void(short)> bar3 = std::bind(bar2, 1, std::placeholders::_1);
std::function<void()> bar4 = std::bind(bar3, 2);
bar4(); // prints "012"
return 0;
}
到现在为止还挺好 .
现在说我想做同样的事情 - 绑定一个函数的第一个参数,获取新函数并重复这个过程直到所有参数都被绑定 - 但是将它推广到不仅用于3个参数的函数,如上面的C示例,但是具有未知*参数数量的函数 .
*在C中存在可变参数,在C 11中存在可变参数模板 . 我在这里指的是可变参数模板 .
基本上,我希望能够做的是编写一个接受任何 std::function
的函数,并递归地将第一个参数绑定到某个值,直到所有参数都被绑定并且可以调用该函数 .
为简单起见,我们假设 std::function
表示一个函数接受任何integral参数并返回void .
该代码可以考虑为先前代码的概括
#include <functional>
#include <iostream>
// terminating case of recursion
void apply(std::function<void()> fun, int i)
{
fun();
}
template<class Head, class... Tail>
void apply(std::function<void(Head, Tail...)> f, int i)
{
std::function<void(Tail...)> g = std::bind(f, i);
apply<Tail...>(g, ++i);
}
void foo(int a, long b, short c)
{
std::cout << a << b << c << std::endl;
}
int main()
{
std::function<void(int, long, short)> bar1 = foo;
apply<int, long, short>(bar1, 0);
return 0;
}
这段代码很棒 . 这正是我想要的 . 它不编译 .
main.cpp: In instantiation of 'void apply(std::function<void(Head, Tail ...)>, int) [with Head = int; Tail = {long int, short int}]':
main.cpp:24:40: required from here
main.cpp:12:56: error: conversion from 'std::_Bind_helper<false, std::function<void(int, long int, short int)>&, int&>::type {aka std::_Bind<std::function<void(int, long int, short int)>(int)>}' to non-scalar type 'std::function<void(long int, short int)>' requested
std::function<void(Tail...)> g = std::bind(f, i);
^
问题是你不能在 std::bind
这样的电话中忽略 std::placeholders
. 它们是必需的, std::bind
中的占位符数应与函数中非绑定参数的数量相匹配 .
如果我们改变线
std::function<void(Tail...)> g = std::bind(f, i);
至
std::function<void(Tail...)> g = std::bind(f, i, std::placeholders::_1, std::placeholders::_2);
我们看到它成功通过第一次 apply()
调用,但在第二次传递时卡住,因为在第二次传递期间 g
只需要一个占位符,而我们在 std::bind
中仍然有两个占位符 .
main.cpp: In instantiation of 'void apply(std::function<void(Head, Tail ...)>, int) [with Head = long int; Tail = {short int}]':
main.cpp:13:30: required from 'void apply(std::function<void(Head, Tail ...)>, int) [with Head = int; Tail = {long int, short int}]'
main.cpp:24:40: required from here
main.cpp:12:102: error: conversion from 'std::_Bind_helper<false, std::function<void(long int, short int)>&, int&, const std::_Placeholder<1>&, const std::_Placeholder<2>&>::type {aka std::_Bind<std::function<void(long int, short int)>(int, std::_Placeholder<1>, std::_Placeholder<2>)>}' to non-scalar type 'std::function<void(short int)>' requested
std::function<void(Tail...)> g = std::bind(f, i, std::placeholders::_1, std::placeholders::_2);
^
有一种方法可以使用常规的非可变参数模板来解决这个问题,但它会对 std::function
可以拥有多少个参数进行限制 . 例如,仅当 std::function
具有3个或更少的参数时,此代码才有效
(替换前面代码中的 apply
函数)
// terminating case
void apply(std::function<void()> fun, int i)
{
fun();
}
template<class T0>
void apply(std::function<void(T0)> f, int i)
{
std::function<void()> g = std::bind(f, i);
apply(g, ++i);
}
template<class T0, class T1>
void apply(std::function<void(T0, T1)> f, int i)
{
std::function<void(T1)> g = std::bind(f, i, std::placeholders::_1);
apply<T1>(g, ++i);
}
template<class T0, class T1, class T2>
void apply(std::function<void(T0, T1, T2)> f, int i)
{
std::function<void(T1, T2)> g = std::bind(f, i, std::placeholders::_1, std::placeholders::_2);
apply<T1, T2>(g, ++i);
}
但是该代码的问题在于我必须定义一个新的 apply
函数以支持带有4个参数的 std::function
,然后使用5个参数,6依旧相同 . 更不用说我的目标是不对参数的数量有任何硬编码限制 . 所以这是不可接受的 . 我不希望它有限制 .
I need to find a way to make the variadic template code (the second code snippet) to work.
如果只有 std::bind
不需要指定占位符 - 一切都会起作用,但是当 std::bind
当前有效时,我们需要找到一些方法来指定合适的占位符数 .
知道我们可以找到适当数量的占位符来指定C 11的 sizeof...
可能是有用的 .
sizeof...(Tail)
但是出于这个事实,我无法得到任何有 Value 的东西 .
4 回答
首先,除非你绝对需要,否则请停止使用
bind
.接下来,如果必须,只需键入erase:
现在我们有一些机器,一个基础案例:
其中说“如果我们可以被调用,只需调用
f
.接下来,递归案例 . 它依赖于C 14返回类型扣除:
如果你想增加,请传递
[](auto&&x){return x+1;}
作为第3个arg .如果您不想更改,请将
[](auto&&x){return x;}
传递给第3个arg .这些代码都没有被编译,因此可能存在拼写错误 . 我也担心C 14返回类型演绎的递归递归,有时会变得棘手 .
如果你真的必须使用
bind
,你可以通过专门化std::is_placeholder
来定义你自己的占位符类型:这有用的原因是它允许您在编译时将整数映射到占位符,您可以使用
integer_sequence
技巧:Demo .
make_integer_sequence
和朋友是C 14,但可以在C 11中轻松实现 .如果您准备放弃
std::bind
(在我看来,这对于C11之前的部分应用程序来说真的是一个hacky变通方法),这可以非常简洁地写出来:在C 11模式下使用clang 3.4和g 4.8.2进行测试 . 还on ideone .
您不需要递归地使用
std::bind
来调用具有参数元组的某个函数,可以使用参数索引来评估这些值:使用示例:
Live demo
正如@T.C.所述in his answer
std::make_index_sequence
是C 14特征,但it can be implemented in C++11 .