模板非类型参数,C ++ 11,字符串文字的限制

时间:2015-02-27 10:58:28

标签: c++ templates iso string-literals non-type

模板非类型参数的限制规则说:

非类型非模板模板参数的模板参数应为以下之一:

- 对于整数或枚举类型的非类型模板参数,是模板参数类型的转换常量表达式(5.19);或

- 非类型模板参数的名称;或

- 一个常量表达式(5.19),用于指定具有静态存储持续时间和外部或内部链接的对象的地址,或具有外部或内部链接的函数,包括函数模板和函数模板ID,但不包括非静态类成员,表达(忽略括号)为 &安培; id-expression,除了&如果名称引用函数或数组,则可以省略,如果相应的模板参数是引用,则应省略;或

- 一个求值为空指针值的常量表达式(4.10);或

- 一个求值为空成员指针值的常量表达式(4.11);或

- 指向成员的指针,如5.3.1所述。

2 [注意:字符串文字(2.14.5)不满足任何这些类别的要求,因此不是可接受的模板参数。

[ Example:
template<class T, const char* p> class X {
/ ... /
};
X<int, "Studebaker"> x1; // error: string literal as template-argument
const char p[] = "Vivisectionist";
X<int,p> x2; // OK
—end example ] —end note ]

那么为什么字符串文字不能用作非类型参数的参数?

const char arr[5] = "1234";

arr具有相同的类型 const char [5]

"1234"; 

arr具有外部链接,这就是为什么允许在c ++ 11标准之前使用arr作为非类型模板参数。

但是现在指向具有内部链接(静态存储)的对象的指针也允许用作非类型模板参数,而字符串文字具有内部链接。

2 个答案:

答案 0 :(得分:3)

从你的问题来看,最接近字符串文字的是:

  

一个常量表达式(5.19),用于指定具有静态存储持续时间和外部或内部链接的对象的地址

字符串文字既没有外部链接也没有内部链接,因此不允许使用它们。

如果您有多个翻译单元,每个单元都包含带有内部链接的const char arr[5];定义,那么这些单元都是不同的对象,具有不同的地址,但始终位于单个翻译单元arr == arr内。实现找到了如何使模板参数工作。

如果您有多个翻译单元,每个单元都包含"1234",则保证具有不同的地址。然而,即使在单个翻译单元中,他们也 保证具有相同的地址。

如果"1234" != "1234",则引用模板S<"1234">毫无意义:您每次都指的是不同的模板实例。

如果"1234" == "1234",那么实施就会变得复杂,以确保S<"1234">在每个翻译单元中都是相同的类型。

答案 1 :(得分:0)

下面的有效代码示例也很有趣:

#include <iostream>

template<const char* STR>
void print() {std::cout << STR << std::endl;}

const char str[] = "HELLO WORLD!";

int main()
{
    //fails: print<reinterpret_cast<const char*>("HELLO WORLD!")>();
    print<reinterpret_cast<const char*>(str)>();
}

最后,编译器会将内联字符串文字(如在注释行中)放置为 .rodata 部分中的新变量。每次都会导致发出新的临时变量和引用该变量地址的新模板实例。对于内容相等的常量字符串文字,在链接时优化之后,所有可变地址以后都将指向相同的地址。即使当今的C ++编译器不允许这样做(很可能是因为没有充分的理由或存在一些隐藏的极端情况),但似乎有可能允许这种构造。在某些情况下,这种功能可以帮助简化编译时配置。

将临时类实例化的地址作为NT模板参数接受会朝着相似的方向发展:

#include <iostream>

struct S {int a, b, c;};

template<const S* P>
void print() {std::cout << P->a << P->b << P->c << std::endl;}

const S s{1, 2, 3};

int main()
{
    //fails: print< &S{1, 2, 3} >();
    print<&s>();
}