线性编译时算法将可变参数模板转换为函数调用

时间:2014-03-29 03:51:07

标签: templates c++11 variadic-templates

我有一个可变参数模板函数,它接受任意数量的参数。我需要将这些参数转换为一系列函数调用,其中顺序很重要。以下方法有效:

#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;的东西。)

2 个答案:

答案 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;
}