C++模板学习笔记

C++模板 #

基本术语 #

模板形参

模板特化

模板实例化

变参模板 #

变参函数模板 #

template <typename T>
T min(T a, T b)
{
  return a < b ? a : b;
}

template <typename T, typename... Args>
T min(T a, Args... args)
{
  return min(a, min(args...));
}

形参包中的实参数量可在编译器通过sizeof...运算符获取,此运算符返回一个类型为std::size_tconstexpr值。

template <typename T, typename... Args>
T sum(T a, Args... args)
{
  if constexpr (sizeof... (args) == 0)
    return a;
  else
    return a + sum(args...);
}

sizeof...(args)sizeof...(Args)不等价,前者是用于形参包args上的sizeof运算符,后者是形参包argssizeof运算符上的展开。

template <typename... Ts>
constexpr auto get_type_sizes()
{
  return std::array<std::size_t, sizeof...(Ts)>{sizeof(Ts)...};
}
auto sizes = get_type_sizes<short, int, long, long long>();

sizeof...(Ts)在编译期求值为4,而sizeof(Ts)...则展开为以逗号分隔的形参包:sizeof(short), sizeof(int), sizeof(long), sizeof(long long)

变参类模板 #

template <typename T, typename... Ts>
struct tuple
{
  tuple(T const& t, Ts const &... ts)
    : value(t), rest(ts...)
  {}

  constexpr int size() const { return 1 + rest.size(); }

  T            value;
  tuple<Ts...> rest;
};
template <typename T>
struct tuple<T>
{
  tuple(const T& t)
    : value(t)
  {}

  constexpr int size() const { return 1; }

  T value;
}

折叠表达式 #

折叠表达式是一种涉及形参包的表达式,在二元运算符上折叠(或减少)形参包中的元素。

template <typename T>
T sum(T a) { return a; }

template <typename T, typename... Args>
T sum(T a, Args... args)
{
  return a + sum(args...);
}

可通过折叠表达式简化为:

template <typename... T>
int sum(T... args)
{
  return (... + args);
}

(... + args)即为折叠表达式,圆括号不能省略,其在求值后变为((((arg0 + arg1) + arg2) + ...) + argN)。 总共有4种不同类型的折叠表达式:

折叠语法展开
一元右折叠(pack op …)(arg1 op (… op (argN-1 op argN)))
一元左折叠(… op pack)(((arg1 op arg2) op …) op argN)
二元右折叠(pack op … op init)(arg1 op (… op (argN-1 op (argN op init))))
二元左折叠(init op … op pack)((((init op arg1) op arg2) op …) op argN)

sum中形参包args直接出现在折叠表达式中。形参包也可以成为表达式的一部分如下:

template <typename... T>
void printl(T... args)
{
  (..., (std::cout << args)) << '\n';
}

template <typename... T>
void printr(T... args)
{
  ((std::cout << args), ...) << '\n';
}

printl('d', 'o', 'g'); // (((std::cout<<'d'), std<<cout<<'o'), std::cout<<'g') => dog
printr('d', 'o', 'g'); // (std::cout<<'d', (std::cout<<'o', (std::cout<<'g'))) => dog

形参包args(std::cout << args)表达式的一部分,这不是一个折叠表达式,((std::cout << args), ...)才是折叠表达式,是逗号运算符上的一元左折叠表达式。

SFINAE 和 enable_if #

SFINAE(Subsitution Failure Is Not An Error)。