Chapter4 Variadic Templates

Fold Expression (Since C++17)

fold_expression 例如,在有fold表达式支持时,可以这样写:

template<typename... Args>
void print(Args... args)
{
  (std::cout << ... << args);
}

然而,在此之前要繁琐一些

template<typename... Args>
void print(Args... args)
{
  int arr[] = {((std::cout << args), 0)...};
}

fold表达式的详细简绍参考n4659: expr.prim.fold,或者自行Google。

Variadic Indices

一个例子:

template<size_t... Args> struct seq {};

template<size_t N, size_t... Index> struct make_seq
{
  using type = typename make_seq<N - 1, N - 1, Index...>::type;
};

template<size_t... Index> struct make_seq<0, Index...>
{
  using type = seq<Index...>;
};

template<typename T> struct add_space
{
  add_space(const T& t) : t_{t} {}
  const T& t_;
  friend std::ostream& operator<< (std::ostream& os, add_space<T> x)
  {
    return os << x.t_ << ' ';
  }
};

template<size_t... Idx> void print(seq<Idx...>)
{
  (std::cout << ... << add_space(Idx)) << '\n';
}

int main()
{
  print(make_seq<10>::type{}); // print `0 1 2 3 4 5 6 7 8 9`
}

variadic indices常常与std::tuplestd::variantstd::get等一起使用。

Variadic Deduction Guide

C++17允许将类型推导运用于可变参数,例如:

#include <type_traits>

template<typename T, int N>
struct Array
{
  template<typename A, typename... Args>
  Array(A a, Args... args)
    : arr_{a, args...}
  {}
  T arr_[N];
};

int main()
{
  Array arr {1, 2, 3, 4, 5}; // no implicit deduction guide available.
}

尝试编译会出现一下错误

x.cc: In function ‘int main()’:
x.cc:17:27: error: class template argument deduction failed:
   Array arr {1, 2, 3, 4, 5};
                           ^
x.cc:17:27: error: no matching function for call to ‘Array(int, int, int, int, int)’
x.cc:7:3: note: candidate: template<class T, int N, class A, class ... Args> Array(A, Args ...)-> Array<T, N>
   Array(A a, Args... args)
   ^~~~~
x.cc:7:3: note:   template argument deduction/substitution failed:
x.cc:17:27: note:   couldn't deduce template parameter ‘T’
   Array arr {1, 2, 3, 4, 5};

注意到错误提示中这句话note: candidate: template<class T, int N, class A, class ... Args> Array(A&&, Args ...)-> Array<T, N>,这说明编译器尝试推导variadic template,但是失败了。这是就需要显示的告诉编译器正确的推导规则:

template<typename T, typename... U> Array(T, U...) -> Array<std::enable_if_t<(std::is_same_v<T, U> && ...), T>, (1 + sizeof...(U))>;

其中enable_if_t利用SFINAE确保所有可变参数都是同一类型的(std::is_same_v<T, U> && ...)则是利用fold expression完成对所有参数的检查,最后(1 + sizeof...(U))则确定Array的大小。

Variadic Base Classes