接受函子/可调用对象的可变参数函数
Variadic Function Accepting Functors/Callable Objects
>问题
我希望创建一个函数,它接受任意数量的函子对象,或者更一般地只接受可调用对象(不同类型的(并将它们应用于内部数据结构。该函数将在代码的不同点与不同数量的函子一起使用。
与其制作不同的版本来接受 1,2,3... 等和重复代码,我想过使用可变参数模板。
我找到了一个解决方案,我将在下面发布作为答案,因为我在 Google 上找不到任何关于这个的内容,其他人可能会发现它很有用。但是,请,如果有人有任何更好的想法,请发布它们。我有一种感觉,应该有一个性病的方法可以做到这一点吗?
幼稚的尝试
我有点知道这行不通,但我的第一次尝试是
#include <iostream>
using namespace std;
struct FunctorA {
void operator() () {
cout << "FunctorA" << endl;
}
};
struct FunctorB {
void operator() () {
cout << "FunctorB" << endl;
}
};
template<typename... Fs>
void apply_functors(Fs... fs) {
fs()...; // will not work - can only expand packs in certain situations (e.g. as funciton
}
int main(void) {
apply_functors(FunctorA(),FunctorB());
apply_functors(FunctorA());
apply_functors([]()->void{ cout << "Lambda" << endl; });
return 0;
}
但是,这是行不通的,因为我们只允许在某些情况下扩展参数包,而像这样免费的参数包不是一个。
尝试 2
我的下一个想法是制作一个虚拟函数,它什么都不做,但我可以将函子作为参数传递到其中,然后将它们扩展。这背后的原因是函数参数是我们可以扩展参数包的情况之一。
template<typename... Fs>
void _dummyF_impl(Fs... fs){}
template<typename... Fs>
void apply_functors(Fs... fs) {
_dummyF_impl( fs()... ); // only works if all functors return a value (else we get "invalid use of 'void'")
}
但是,这将不起作用,因为函子从其operator()
返回 void,并且我们不能将 void 作为参数传递给函数。
大括号初始值设定项中扩展参数包具有保证从左到右计算的额外好处(函数参数列表并非如此(。您还应该考虑使用完美的转发,原因如下。
#include <initializer_list> //
#include <utility> // std::forward
template<typename... Fs>
void apply_functors(Fs&&... fs)
{
auto list = { (std::forward<Fs>(fs)(), 0)... };
// ^^^^ deduced as std::initializer_list
}
原则上是相同的,但这次没有必要包含<initializer_list>
是一个可变参数模板构造函数,您也可以使用大括号初始值设定项调用它:
struct dummy {
template<typename... Fs>
dummy(Fs&&... fs) { }
};
template<typename... Fs>
void apply_functors(Fs&&... fs)
{
dummy { (std::forward<Fs>(fs)(), 0)... }; // evaluated left-to-right, too
}
我会这样做
template<typename... Fs>
void apply_functors(Fs... fs) {
int i[] = { ((void) fs(), 0)... };
(void) i; // prevents a warning about i not being used.
}
发生参数包扩展的情况之一是大括号初始值设定项内部。上面我在数组初始化中使用它。
模式(fs(), 0)...
扩展到(f1(), 0), (f2(), 0), ..., (fn(), 0)
其中f1
、...fn
是提供的可调用对象。请注意,计算表达式(f1(), 0)
调用 f1()
时会忽略其返回值,表达式的结果是 0
(int
(。
需要更复杂的模式((void) fs(), 0)...
来防止极端情况,即当fs()
返回逗号运算符重载的类型时。
相对于使用虚拟函数的解决方案,额外的优点是,函数参数的计算顺序不是由标准指定的,而在数组初始化中是(从左到右(。例如,请注意,在 Dan 的解决方案中调用 apply_functors(FunctorA(),FunctorB());
输出FunctorB
在FunctorA
之前,而在此解决方案中,输出FunctorA
后跟 FunctorB
。
更新:阅读jrok的解决方案后,我意识到需要完美的转发。所以我更新的解决方案将是
template<typename... Fs>
void apply_functors(Fs&&... fs) {
int i[] = { ((void) std::forward<Fs>(fs)(), 0)... };
(void) i; // prevents a warning about i not being used.
}
我的解决方案
我找到的解决方案是逗号运算符。我以前从来没有真正的理由使用它,但它非常适合这里。
逗号运算符可用于计算单独的语句,但随后"返回"最后一个表达式的值,因此a,b,c
计算结果为 c
。
因此,我们可以使用它来评估我们的函子,忽略 void 返回值,然后返回传递给 _dummyF_impl
的东西:
template<typename... Fs>
void apply_functors(Fs... fs) {
_dummyF_impl( (fs(),0)... );
}
这在 g++ 4.7.3 下编译良好,运行时输出符合我们的预期:
FunctorB
FunctorA
FunctorA
Lambda
- 在不传递参数数量且只有3个点的情况下,如何使用变差函数
- 如何使用可变参数模板强制转换每个变体类型
- 关于如何在具有单个参数的变体构造中选择替代方案?
- 调用参数排列不变函数 f(i++, i++)
- 参数归纳与标准::变体
- 模板化回调参数的逆变,如 C# 中的逆变
- 如何在没有参数包的情况下编写变差函数
- 通过具有嵌套类的工厂类获取多个变异类模板参数包
- 获取模板参数的成员变量值列表
- 保留短 lambda 用作函数的中间参数,使用 clang 格式保持不变
- 如何定义变体<x,y,z>提取模板参数的子类型
- 正确对齐内存模板,参数顺序不变
- 递归中不同参数类型的变元模板函数
- 通过函数指针传递给变差函数的参数会更改其值
- 提升预定义为带有参数的全局 lambda 的变体访问者
- 使用可变参数模板参数提升变体访问者
- boost ::变体 - 为什么模板参数比const字符串参数具有更高的优先级
- 将变参数包中的值加载到临时数组中
- 使用额外参数提升变体访客
- 从变长参数列表中提取std::string