我有一个可变参数模板函数,它接受任意数量的参数。我需要将这些参数转换为一系列函数调用,其中顺序很重要。以下方法有效:
#include <iostream>
#include <typeinfo>
#include <vector>
void printType(const std::type_info& ti) {
printf("%s\n", ti.name());
}
void expandVariadic() {
}
template<typename First, typename... Rest>
void expandVariadic(const First& first, const Rest&... rest) {
printType(typeid(First));
expandVariadic(rest...);
}
int main(int argc, const char* argv[]) {
expandVariadic(10, "hi", std::cout, std::vector<int>());
return 0;
}
但是,上面的代码扩展为传递给expandVariadic的二次参数。我可以强制内联expandVariadic,但这仍然意味着二次编译时间。
是否采用线性时间方法来实现上述目标?
(它们需要是函数调用:想象一个生成一系列输出调用的记录器,或者用类型信息填充向量&lt;&gt;的东西。)
答案 0 :(得分:4)
struct sink{template<class T>sink(T&&){};};
void run_in_order(){}
template<typename... Fs>
void run_in_order(Fs&&...fs){
sink _[]={((void)std::forward<Fs>(fs)(),0)...};
sink{_};// warning elimination
}
使用:
template<typename... Ts>
void expandVariadic(const Ts&... ts) {
run_in_order([&]{printType(typeid(ts));}...);
}
很好地隐藏了run_in_order
内的魔法。 run_in_order
接受一系列lambdas或其他nullary可调用对象,并按从左到右的顺序调用它们。这是有效的,因为C ++ 11标准保证{}
行的sink _[]
内的评估是从左到右。
我将返回值转换为void
,然后在RHS上调用带有0
的逗号运算符,以保证我们得到一个可以转换为sink
的值。 (void)
强制转换处理lambda传入的极端情况返回一个覆盖operator,
的对象。
这种技术的缺点是对lambdas内部包的支持不如支持其他包扩展。我调查过的许多编译器已经能够对大多数代码进行参数包扩展,但是当有问题的参数包在lambda内部并且扩展在外面时失败了。
答案 1 :(得分:1)
在调用任何函数时,存在一个经常看到的扩展参数包而不递归的技巧:here it is
#include <iostream>
#include <typeinfo>
#include <vector>
void printType( const std::type_info& ti ) {
std::cout << ti.name() << '\n';
}
// because cast to void is a non generic way to hide unused variable warning.
template <typename T> void ignore(T&&) {}
template<typename... Ts>
void expandVariadic(Ts&&... ts) {
// expand by using a initializer list.
auto il { ( printType( typeid(std::forward<Ts>(ts)) ), 0)... };
ignore(il);
}
int main(int argc, const char* argv[]) {
expandVariadic(10, "hi", std::cout, std::vector<int>());
return 0;
}