非类型模板参数


93

我知道非类型模板参数应该是一个常数整数表达式。有人可以阐明为什么吗?

template <std::string temp>
void foo()
{
     // ...
}
error C2993: 'std::string' : illegal type for non-type template parameter 'temp'.

我知道什么是常数积分表达式。不允许std::string使用上述片段中的非常量类型的原因是什么?


17
模板参数在编译时解析。
Etienne de Martel

Answers:


121

之所以不能这样做,是因为在编译期间无法解析和替换非常量表达式。它们可能会在运行时更改,这将需要在运行时生成新模板,这是不可能的,因为模板是编译时的概念。

这是标准允许非类型模板参数(14.1 [temp.param] p4)的内容:

非类型模板参数应具有以下(可选,具有cv限定)类型之一:

  • 整数或枚举类型,
  • 指向对象或函数的指针,
  • 对对象的左值引用或对函数的左值引用,
  • 指向成员的指针,
  • std::nullptr_t

6
@ALOToverflow:这属于“指向成员的指针”。它是“指向成员函数的指针”或“指向成员数据的指针”。
Xeo

4
值得注意的是,对于指向对象(或实例字段)的指针,对象必须具有静态存储持续时间和链接(C ++ 11之前的外部,C ++ 11中的内部或外部),以便指向它们的指针可以在编译时创建。
西奥多·默多克

2
在C ++ 20中,现在可以允许这种情况,只要该类型具有强大的结构相等性,为文字,没有可变/易变的子对象且航天器运算符是公共的即可。
Rakete1111 '18

73

那是不允许的。

但是,这是允许的:

template <std::string * temp> //pointer to object
void f();

template <std::string & temp> //reference to object
void g();

参见C ++ Standard(2003)中的§14.1/ 6,7,8。


插图:

template <std::string * temp> //pointer to object
void f()
{
   cout << *temp << endl;
}

template <std::string & temp> //reference to object
void g()
{
     cout << temp << endl;
     temp += "...appended some string";
}

std::string s; //must not be local as it must have external linkage!

int main() {
        s = "can assign values locally";
        f<&s>();
        g<s>();
        cout << s << endl;
        return 0;
}

输出:

can assign values locally
can assign values locally
can assign values locally...appended some string

7
@Mahesh:因为模板基本上在模板上使用的是该std::string指针或引用对象的地址。如果该变量是局部变量,则每次调用该函数时,您可能会获得不同的地址。
Xeo

11
@Mahesh:您不知道在编译时调用堆栈是什么样的。在您的函数之前,可能已经调用了10个其他或3个或更多个其他名称,因此在堆栈上创建字符串的地址可以在每次调用之间更改。当您具有带有外部链接的对象时,其地址在编译/链接期间是固定的。
Xeo

2
@Xeo“ 其地址在编译/链接期间固定。 ”否,对于可重定位或与位置无关的代码。
curiousguy13 2013年

1
这个答案(当前)似乎没有解决OP的问题,OP的问题是为什么存在这种行为。这个答案只是重申了OP的示例,没有提供任何解释。
Quuxplusone

1
我参加聚会很晚,看来智能指针也不起作用
Nicholas Humphrey

28

您需要能够处理模板参数

template <std::string temp>
void f() {
 // ...
}

f<"foo">();
f<"bar">(); // different function!?

现在,一个impl将需要为一个std::string或任何其他由用户定义的任意类定义一个唯一的字符序列,并存储一个特定的值,其含义对于实现而言是未知的。另外,在编译时无法计算任意类对象的值。

计划考虑允许将文字类类型用作C ++ 0x后的模板参数类型,并通过常量表达式对其进行初始化。可以通过使数据成员根据其值递归地对其进行处理(例如,对于基类,我们可以应用深度优先,从左到右遍历)。但这绝对不适用于任意类。


10

模板参数列表中提供的非类型模板参数是一个表达式,其值可以在编译时确定。这样的论点必须是:

常量表达式,具有外部链接的函数或对象的地址或静态类成员的地址。

另外,字符串文字是具有内部链接的对象,因此您不能将它们用作模板参数。您也不能使用全局指针。考虑到四舍五入错误的明显可能性,不允许使用浮点文字。


您使用了引号,那么来源是什么?如果它有报价来源,我想对其进行投票。
加布里埃尔·斯台普斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.