03 非类型模板参数

非类型的类模板参数

template
class Stack {
 public:
  Stack();
  void push(const T&);
  void pop();
  const T& top() const;
  bool empty() const { return n == 0; }
  std::size_t size() const { return n; }
 private:
  std::array v;
  std::size_t n;
};

template
Stack::Stack() : n(0) // 默认初始化元素数为0
{}

template
void Stack::push(const T& x)
{
  assert(n < Maxsize); // 确定Stack未满
  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];
}
  • 使用该模板需要同时指定类型和个数
int main()
{
  Stack intStack; // 20个int的Stack
  intStack.push(42);
  std::cout << intStack.top(); // 42
  intStack.pop();

  Stack stringStack;
  stringStack.push("hi");
  std::cout << stringStack.top(); // hi
  stringStack.pop();
}
  • 非类型模板参数也可以指定默认值
template
class Stack {
  …
};

非类型的函数模板参数

template
T addValue(T x)
{
  return x + N;
}
  • 这类模板通常用作函数参数
std::vector v{ 0, 1, 2};
std::vector v2(3);
std::transform(v.begin(), v.end(), v2.begin(), addValue<1, int>);
for (auto x : v2) std::cout << x; // 123
  • 也能定义一个模板参数,由该参数之前的参数推断类型
template
T addValue(T x)
{
  return x + N;
}
  • 或者保证传值类型相同
template
T addValue(T x)
{
  return x + N;
}

非类型模板参数的限制

  • 非类型模板参数可以是整型(包括枚举值)或指向外部链接对象的指针,但不能是浮点数和类对象
template // 错误:非类型模板参数不能是浮点数
double f(double x)
{
  return x * N;
}
 
template // 错误:非类型模板参数不能是类对象
class A
{};
  • 也不能用字符串字面值常量、临时对象、数据成员或其他子对象作模板实参
template
class A
{};
 
A<"hi"> x;  // 错误:不允许字符串字面值常量作实参
// 使用指针也不行
const char* s = "hi";
const char s2[] = "hi";
A x; // 错误:s是internal linkage对象的指针
A x; // 错误
  • C++的每个版本逐渐放宽了限制,C++11中对象有external linkage即可,C++14中对象有external linkage或internal linkage即可,C++17不需要linkage
// 以下任何用作模板实参的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
}
  • 非类型模板参数的实参可以是任何编译期表达式
template
class A
{};

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

auto非类型模板参数

  • C++17允许将非类型模板参数定义为auto,以接收任何允许作为非类型模板参数的类型
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作为返回类型
// 如果在类外定义size成员函数要写为
template
typename Stack::size_type Stack::size() const
{
  return n;
}

// C++14中可写为
template
auto Stack::size() const
{
  return n;
}

int main()
{
  Stack intStack;
  Stack stringStack;
  auto x = intStack.size();
  auto y = stringStack.size();
  if (!std::is_same_v)
  {
    std::cout << "size types differ" << '\n';
  }
}
  • auto非类型模板参数仍然不能是浮点数,auto只接收允许作为非类型模板参数的类型
Stack s; // 错误:非类型模板参数不能是浮点数
  • 此外auto比常规情况多出的一点限制是,auto不能接受const char[]
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)让decltype进行推断即可解决此问题
template
class A {
 public:
  void print() { std::cout << x; }
};

你可能感兴趣的:(03 非类型模板参数)