随着时间的推移,c++的不同版本放宽了用作模板参数的规则,而在c++ 17中,这种情况再次发生。现在可以使用模板,而不需要在当前范围之外定义它们。
非类型模板参数只能是常量整数值(包括枚举)、指向对象/函数/成员的指针、指向对象或函数的lvalue引用,或者std::nullptr_t (nullptr的类型)。
对于指针,需要链接,这意味着不能直接传递字符串文本。但是,自从c++ 17起,可以使用具有内部链接的指针。例如:
#include <iostream>
template<const char* str>
class Message
{
public:
Message() :length{ std::strlen(str) }
{
}
private:
std::size_t length{ 0 };
};
extern const char hello[] = "external linkage"; // external linkage
const char hello11[] = "internal linkage!"; // internal linkage
int main()
{
Message<hello> msg; // OK (all C++ versions)
Message<hello11> msg11; // OK since C++11
static const char hello17[] = "no linkage"; // no linkage
Message<hello17> msg17; // OK since C++17
return 0;
}
结果如下:
也就是说,自从c++ 17起,仍然需要两行代码来将字符串文本传递给模板。但是可以将第一行放在与类实例化相同的范围内。
这个特性还解决了一个不幸的约束,从c++ 11起虽然可以传递一个指针到类模板,如下:
template<int* p> struct A
{
};
int num;
A<&num> a; // OK since C++11
但是不能用编译时的constexpr函数返回的地址,C++17中已经支持了:
int num;
...
constexpr int* pNum() {
return #
}
A<pNum()> b; // ERROR before C++17, now OK
完整例子如下:
#include <iostream>
int num;
template<int* p> struct A
{
};
constexpr int* pNum()
{
return #
}
int main()
{
A<&num> a; // OK since C++11
A<pNum()> b; // ERROR before C++17, now OK
return 0;
}
结果如下:
由于pNum返回num的地址,所以变量b的类型也是A<&num>。