模板参数可以是int,float,string等内置类型,也可以是下面非类型参数:

  • 整数:整数模板参数比较常用
  • 枚举
  • 指向对象或函数的指针
  • 对象或函数引用
  • 指向非临时对象成员的指针
  • 非类型模版参数的作用相当于为函数模板和类模板预定义一些常量

非类型类模板参数示例

  1. template<typename T, std::size_t Maxsize>//指定一个整型变量作为模板参数
  2. class Stack {
  3. public:
  4. Stack();
  5. void push(const T&);
  6. void pop();
  7. const T& top() const;
  8. bool empty() const { return n == 0; }
  9. std::size_t size() const { return n; }
  10. private:
  11. std::array<T, Maxsize> v;
  12. std::size_t n;
  13. };
  14. template<typename T, std::size_t Maxsize>
  15. Stack<T, Maxsize>::Stack() : n(0) // 默认初始化元素数为0
  16. {}
  17. template<typename T, std::size_t Maxsize>
  18. void Stack<T, Maxsize>::push(const T& x)
  19. {
  20. assert(n < Maxsize); // 确定Stack未满
  21. v[n] = x;
  22. ++n;
  23. }
  24. template<typename T, std::size_t Maxsize>
  25. void Stack<T, Maxsize>::pop()
  26. {
  27. assert(!v.empty());
  28. --n;
  29. }
  30. template<typename T, std::size_t Maxsize>
  31. const T& Stack<T, Maxsize>::top() const
  32. {
  33. assert(!v.empty());
  34. return v[n - 1];
  35. }
  • 使用该模板需要同时指定类型和个数

    1. int main()
    2. {
    3. Stack<int, 20> intStack; // 20个int的Stack
    4. intStack.push(42);
    5. std::cout << intStack.top(); // 42
    6. intStack.pop();
    7. Stack<std::string, 40> stringStack;
    8. stringStack.push("hi");
    9. std::cout << stringStack.top(); // hi
    10. stringStack.pop();
    11. }
  • 非类型模板参数也可以指定默认值

    1. template<typename T = int, std::size_t Maxsize = 100>
    2. class Stack {
    3. };

非类型函数模板参数示例

  1. template<int N, typename T>
  2. T addValue(T x)
  3. {
  4. return x + N;
  5. }
  • 这类模板通常用作函数参数

    1. std::vector<int> v{ 0, 1, 2};
    2. std::vector<int> v2(3);
    3. std::transform(v.begin(), v.end(), v2.begin(), addValue<1, int>);
    4. for (auto x : v2) std::cout << x; // 123
  • 也能定义一个模板参数,由该参数之前的参数推断类型

    1. template<auto N, typename T = decltype(N)>
    2. T addValue(T x)
    3. {
    4. return x + N;
    5. }
  • 或者保证传值类型相同

    1. template<typename T, T N = T{}>
    2. T addValue(T x)
    3. {
    4. return x + N;
    5. }

非类型模板参数的限制

  • 非类型模板参数可以是整型(包括枚举值)或指向外部链接对象的指针,但不能是浮点数和类对象 ```cpp template // 错误:非类型模板参数不能是浮点数 double f(double x) { return x * N; }

template // 错误:非类型模板参数不能是类对象 class A {};

  1. - **也不能用字符串字面值常量、临时对象、数据成员或其他子对象作模板实参**
  2. ```cpp
  3. template<const char* s>
  4. class A
  5. {};
  6. A<"hi"> x; // 错误:不允许字符串字面值常量作实参
  7. // 使用指针也不行
  8. const char* s = "hi";
  9. const char s2[] = "hi";
  10. A<s> x; // 错误:s是internal linkage对象的指针
  11. A<s2> x; // 错误
  • C的每个版本逐渐放宽了限制
    • C11中对象有external linkage即可
    • C14中对象有external linkage或internal linkage即可
    • C17不需要linkage ```cpp // 以下任何用作模板实参的const char[]改为const char*都会出错 extern const char s03[] = “hi”; // external linkage const char s11[] = “hi”; // internal linkage

int main() { A m03; // OK (all versions) A m11; // OK since C++11 static const char s17[] = “hi”; // no linkage A m17; // OK since C++17 }


- 非类型模板参数的实参可能是任何编译期表达式
```cpp
template<int I, bool B>
class A
{};

A<sizeof(int) + 4, sizeof(int) == 4> a;
  • 如果表达式中使用了大于号,必须把表达式写进小括号中,以防止嵌套的大于号被当作右尖括号
    A<1, sizeof(int) > 4> a; // 错误:大于号被看作右尖括号,于是被视为A<1,sizeof(int)> 4
    A<1, (sizeof(int) > 4)> a; // OK
    

auto非类型模板参数

  • C++17允许将非类型模板参数定义为auto,以接收任何允许作为非类型模板参数的类型 ```cpp template class Stack { public: using size_type = decltype(Maxsize); Stack(); void push(const T&); void pop(); const T& top() const; bool empty() const { return n == 0; } size_type size() const { return n; } private: std::array v; size_type n; };

template Stack::Stack() : n(0) {}

template void Stack::push(const T& x) { assert(n < Maxsize); v[n] = x; ++n; }

template void Stack::pop() { assert(!v.empty()); —n; }

template const T& Stack::top() const { assert(!v.empty()); return v[n - 1]; }


- C++14中允许auto作为返回类型
```cpp
// 如果在类外定义size成员函数要写为
template<typename T, auto Maxsize>
typename Stack<T, Maxsize>::size_type Stack<T, Maxsize>::size() const
{
  return n;
}

// C++14中可写为
template<typename T, auto Maxsize>
auto Stack<T, Maxsize>::size() const
{
  return n;
}

int main()
{
  Stack<int, 20u> intStack;
  Stack<std::string, 40> stringStack;
  auto x = intStack.size();
  auto y = stringStack.size();
  if (!std::is_same_v<decltype(x), decltype(y)>)
  {
    std::cout << "size types differ" << '\n';
  }
}
  • auto非类型模板参数仍然不能是浮点数,auto只接收允许作为非类型模板参数的类型

    Stack<int, 3.14> s; // 错误:非类型模板参数不能是浮点数
    
  • 此外auto比常规情况多出的一点限制是,auto不能接受const char[] ```cpp template class A { public: void print() { std::cout << x; } };

int main() { A<1> a; a.print(); // 1

static const char s[] = “hi”; A b; // 错误 }


- 改用[decltype(auto)](https://zh.cppreference.com/w/cpp/language/auto)让decltype进行推断即可解决此问题
```cpp
template<decltype(auto) x>
class A {
 public:
  void print() { std::cout << x; }
};