递归可变函数模板

Recursive variadic function template

我想编写一个 class 方法,该方法采用模板参数包,但参数为零,"iterate" 类型:

struct Bar {
    template <typename T, typename... Ts>
    void foo() {
        // something with T that involves Bar's members
        foo<Ts...>();
    }
};

实现这个的首选方法是什么?

我喜欢重载函数和使用类型列表:

#include <iostream>
#include <typeinfo>

template <typename ...Ts> struct typelist { };

void foo_impl(typelist<> )
{
  // we are finished
}

template <typename T, typename ...Ts>
void foo_impl(typelist<T, Ts...> )
{
  std::cout << typeid(T).name() << ", ";
  foo_impl(typelist<Ts...>{});
}



template <typename ...Ts>
void foo()
{
  std::cout << "called with <";
  foo_impl(typelist<Ts...>{});
  std::cout << ">" << std::endl;
}


int main()
{
  foo<int, char, float>();
}

您可以使用以下内容:

struct Bar {
    template <typename... Ts>
    void foo() {
        int dummy[] = {0 /*Manage case where Ts is empty*/,
                       (bar<Ts>(), void() /* To avoid overload `operator,` */, 0)...};
        (void) dummy; // suppress warning for unused variable.
    }

    template <typename T>
    void bar()
    {
        // something with T that involves Bar's members
    }

};

在C++17中,可以用折叠表达式简化:

struct Bar {
    template <typename... Ts>
    void foo() {
        (static_cast<void>(bar<Ts>()), ...);
    }

    template <typename T>
    void bar()
    {
        // something with T that involves Bar's members
    }

};
template<class...Fs>
void do_in_order(Fs&&...fs) {
  int _[]={0, ( std::forward<Fs>(fs)(), void(), 0 )...};
  (void)_;
}

隐藏了按从左到右的顺序执行一组函数对象所需的语法。

然后:

struct Bar {
  template <class... Ts>
  void foo() {
    do_in_order([&]{
      using T = Ts;
      // code 
    }...);
  }
};

并且在符合标准的编译器中,我们将从左到右 运行 // codeT 每种类型。

请注意,某些自称是 C++11 编译器的编译器可能无法编译上述内容。

这种技术的优势在于它隐藏了具有清晰名称的函数中令人讨厌的 "expand and evaluate templates" 代码。你写 do_in_order 一次,它通常足以满足几乎每次使用数组扩展技巧的需要。

使用这种深奥的语法而不是 "more simple" 递归解决方案有两个重要原因。

首先,它使优化器的工作变得更容易。优化器有时会在一堆递归调用后放弃。

其次,传统递归函数的函数签名的长度总和以 O(n^2) 的速度增长。如果您使用辅助类型,名称的总长度也是 O(n^2)。除非您小心,否则这会导致编译时间、link 时间和二进制大小膨胀。

在 C++1z 中,一些 "fold" 语法的计划可能会使上述的深奥部分变得不那么深奥。