第十二章 String作为模板参数

一直以来,C++对于哪些类型可以用于模板参数的规则在逐渐放松,C++17更是如此。即使在当前作用域外定义的模板,也可以使用它作为模板参数。

12.1 在模板中使用string

非类型模板参数只能是整数类型(包括枚举),指向对象、函数、成员的指针,对象或者函数的左值引用,以及std::nullptr_t(nullptr的类型)。

对于指针,链接是必须的,这意味着你不能直接传递字符串字面值。然而,从C++11(译注:这里原文是C++17,可能是笔误)开始,你可以传一个内部链接(internal linkage)的指针。比如

template<const char* str>
class Message {
    ...
};
extern const char hello[] = "Hello World!"; // external linkage
const char hello11[] = "Hello World!"; // internal linkage

void foo()
{
    Message<hello> msg; // OK (all C++ versions)
    Message<hello11> msg11; // OK since C++11
    static const char hello17[] = "Hello World!"; // no linkage
    Message<hello17> msg17; // OK since C++17
}

也就是说,从C++17开始,你仍然需要写两行代码来传字符串字面值给模板,但是现在第一行可以放到和类实例化相同的作用域。 这种能力解决了一个很不幸的约束:从C++11开始你可以传指针给类模板:

template<int* p> struct A {
};

int num;
A<&num> a; // OK since C++11

但你不能使用返回一个地址的编译时函数作为模板参数,但是C++17开始允许这么做了:

int num;
...

constexpr int* pNum() {
    return &num;
}
A<pNum()> b; // ERROR before C++17, now OK

12.2 后记

对于所有非类型模板参数允许常量求值这种能力首先由Richard Smith在https://wg21.link/n4198中提出。最后的公认措辞是由Richard Smith在https://wg21.link/n4268中给出。