给定一个函数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 http://www.cplusplus.com/reference/type_traits/is_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::placeholders
in std::bind
就这样打电话。它们是必需的,并且占位符的数量std::bind
应与函数中非绑定参数的数量匹配。
如果我们换线
std::function<void(Tail...)> g = std::bind(f, i);
to
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
功能来支持std::function
有 4 个参数,然后有 5 个参数,6 个等等。更不用说我的目标是不对参数数量进行任何硬编码限制。所以这是不可接受的。我不希望它有限制。
我需要找到一种方法使可变参数模板代码(第二个代码片段)正常工作。
If only std::bind
不需要指定占位符——一切都会工作,但是作为std::bind
目前可行,我们需要找到某种方法来指定正确数量的占位符。
知道我们可以找到正确数量的占位符来用 C++11 指定可能会很有用sizeof...
sizeof...(Tail)
但我无法从这个事实中得到任何有价值的东西。