非类型的类模板参数
template<typename T, std::size_t Maxsize>
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<T, Maxsize> v;
std::size_t n;
};
template<typename T, std::size_t Maxsize>
Stack<T, Maxsize>::Stack() : n(0) // 默认初始化元素数为0
{}
template<typename T, std::size_t Maxsize>
void Stack<T, Maxsize>::push(const T& x)
{
assert(n < Maxsize); // 确定Stack未满
v[n] = x;
++n;
}
template<typename T, std::size_t Maxsize>
void Stack<T, Maxsize>::pop()
{
assert(!v.empty());
--n;
}
template<typename T, std::size_t Maxsize>
const T& Stack<T, Maxsize>::top() const
{
assert(!v.empty());
return v[n - 1];
}
int main()
{
Stack<int, 20> intStack; // 20个int的Stack
intStack.push(42);
std::cout << intStack.top(); // 42
intStack.pop();
Stack<std::string, 40> stringStack;
stringStack.push("hi");
std::cout << stringStack.top(); // hi
stringStack.pop();
}
template<typename T = int, std::size_t Maxsize = 100>
class Stack {
…
};
非类型的函数模板参数
template<int N, typename T>
T addValue(T x)
{
return x + N;
}
std::vector<int> v{ 0, 1, 2};
std::vector<int> v2(3);
std::transform(v.begin(), v.end(), v2.begin(), addValue<1, int>);
for (auto x : v2) std::cout << x; // 123
template<auto N, typename T = decltype(N)>
T addValue(T x)
{
return x + N;
}
template<typename T, T N = T{}>
T addValue(T x)
{
return x + N;
}
非类型模板参数的限制
- 非类型模板参数可以是整型(包括枚举值)或指向外部链接对象的指针,但不能是浮点数和类对象
template<double N> // 错误:非类型模板参数不能是浮点数
double f(double x)
{
return x * N;
}
template<std::string s> // 错误:非类型模板参数不能是类对象
class A
{};
- 也不能用字符串字面值常量、临时对象、数据成员或其他子对象作模板实参
template<const char* s>
class A
{};
A<"hi"> x; // 错误:不允许字符串字面值常量作实参
// 使用指针也不行
const char* s = "hi";
const char s2[] = "hi";
A<s> x; // 错误:s是internal linkage对象的指针
A<s2> 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<s03> m03; // OK (all versions)
A<s11> m11; // OK since C++11
static const char s17[] = "hi"; // no linkage
A<s17> m17; // OK since C++17
}
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,以接收任何允许作为非类型模板参数的类型
template<typename T, auto Maxsize>
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<T, Maxsize> v;
size_type n;
};
template<typename T, auto Maxsize>
Stack<T,Maxsize>::Stack() : n(0)
{}
template<typename T, auto Maxsize>
void Stack<T,Maxsize>::push(const T& x)
{
assert(n < Maxsize);
v[n] = x;
++n;
}
template<typename T, auto Maxsize>
void Stack<T, Maxsize>::pop()
{
assert(!v.empty());
--n;
}
template<typename T, auto Maxsize>
const T& Stack<T,Maxsize>::top() const
{
assert(!v.empty());
return v[n - 1];
}
// 如果在类外定义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[]
template<auto x>
class A {
public:
void print() { std::cout << x; }
};
int main()
{
A<1> a;
a.print(); // 1
static const char s[] = "hi";
A<s> b; // 错误
}
template<decltype(auto) x>
class A {
public:
void print() { std::cout << x; }
};