下面的示例尝试使用引用类型的变量作为非类型模板参数(本身为引用类型)的参数。 Clang,GCC和VC ++都拒绝它。但为什么?我似乎无法在标准中找到任何使其成为非法的内容。
int obj = 42;
int& ref = obj;
template <int& param> class X {};
int main()
{
X<obj> x1; // OK
X<ref> x2; // error
}
source_file.cpp:9:7:错误:引用类型的非类型模板参数&#39; int&amp;&#39;不是一个对象
其他人以类似的方式抱怨。
从标准(C ++ 11的所有引用; C ++ 14似乎没有相关部分的重大变化):
14.3.2 / 1 非类型非模板模板参数的 template-argument 应为以下之一:
...
- 一个常量表达式(5.19),用于指定具有静态存储持续时间和外部或内部链接的对象的地址...表达(忽略括号)为
&
id-expression ,除了如果相应的 template-parameter 是参考<{li>,&
...将被省略...
现在是一个常数表达式:
5.19 / 2 条件表达式是核心常量表达式,除非它涉及下列其中一项作为潜在评估的子表达式(3.2) )...
...
- id-expression ,引用引用类型的变量或数据成员,除非引用具有先前的初始化,使用常量表达式初始化
...
据我所知,ref
中的X<ref>
是 id-expression ,它引用了引用类型的变量。此变量具有先前的初始化,使用表达式obj
初始化。我相信obj
是一个不变的表达式,无论如何,如果它不是,那么X<obj>
也不应该编译。
X<ref>
无效,而X<obj>
有效?答案 0 :(得分:8)
正确的说法是引用的名称是 id-expression ; id-expression 不引用引用所引用的内容,而是引用引用本身。
int a = 0;
int& ref = a; // "ref" is an id-expression, referring to `ref` - not `a`
您在帖子中引用了标准的相关部分,但是您遗漏了最重要的部分(强调我的):
14.3.2p1 模板非类型参数 [temp.arg.nontype]
非类型非模板模板参数的 template-argument 应为以下之一:
<子> ... 子>
一个常量表达式(5.19),用于指定具有静态sturage持续时间和外部或内部链接的完整对象的地址,或具有外部或内部链接的函数,包括函数模板和函数 template-ids 但不包括非静态类成员,表示(忽略括号)为
&
id-expression ,其中 id-expression 是对象或函数的名称,但如果名称引用函数或数组则可省略&
,如果相应的 template-parameter则应省略T
是一个参考; <子> ... 子>
注意:在早期草稿中“其中id-expression是对象或函数的名称”不存在;它由DR 1570解决 - 这无疑使意图更加明确。
你是绝对正确的;引用本身具有引用类型,并且只能在表达式的一部分时充当对象。
5p5 表达式 [expr]
如果表达式最初具有“
T
引用”类型(8.3.2,8.5.3),则在进行任何进一步分析之前将类型调整为&id-expression
。表达式指定由引用表示的对象或函数,表达式是左值或x值,具体取决于表达式。
非常重要的是要注意常量表达式(“指定完整对象的地址......”)必须是{{1}之一},或id-expression
。
即使常量表达式(不仅仅是 id-expression )可能引用具有静态存储持续时间的对象,我们也无法将其用于< em>“初始化” 引用的模板参数 - 或指针类型。
示例代码段
template<int&>
struct A { };
int a = 0;
constexpr int& b = (0, a); // ok, constant-expression
A<(0, a)> c = {}; // ill-formed, `(0, a)` is not an id-expression
注意:这也是我们无法将 string-literals 用作 template-arguments 的原因;它们不是 id-expressions 。