引用为非类型模板参数

Igo*_*nik 14 c++ language-lawyer c++11

下面的示例尝试使用引用类型的变量作为非类型模板参数(本身为引用类型)的参数.Clang,GCC和VC++都拒绝它.但为什么?我似乎无法在标准中发现任何使其成为非法的内容.

int obj = 42;
int& ref = obj;

template <int& param> class X {};

int main()
{
    X<obj> x1;  // OK
    X<ref> x2;  // error
}
Run Code Online (Sandbox Code Playgroud)

实例


克朗说:

source_file.cpp:9:7:错误:引用类型'int&'的非类型模板参数不是对象

其他人以类似的方式抱怨.


从标准(C++ 11的所有引用; C++ 14似乎没有相关部分的重大变化):

14.3.2/1模板参数的用于非类型,非模板模板参数应是以下之一:

...

  • 一个常量表达式(5.19),用于指定具有静态存储持续时间和外部或内部链接的对象的地址...表示(忽略括号)为& id-expression,但&如果相应的模板参数将被省略... 是一个参考

...

现在什么是常量表达式:

5.19/2条件表达式是一个核心常量表达式除非它涉及以下作为一个潜在的评价子表达式(3.2)中的一个...

...

  • 一个id-expression,它引用引用类型的变量或数据成员,除非引用具有先前的初始化,并使用常量表达式初始化

...


据我所知,refin X<ref>是一个id-expression,它引用了一个引用类型的变量.此变量具有先前的初始化,使用表达式初始化obj.我相信obj是一个不变的表达式,无论如何,如果不是,那么X<obj>也不应该编译.

  • 那我错过了什么?
  • 标准中的哪个子句呈现X<ref>无效,同时X<obj>有效?

Fil*_*efp 8

介绍

但是,引用的名称是id-expression是正确的; 该ID表达并不是指任何引用被引用,但引用本身.

 int    a = 0;
 int& ref = a; // "ref" is an id-expression, referring to `ref` - not `a`
Run Code Online (Sandbox Code Playgroud)

标准(N4140)

您在帖子中引用了标准的相关部分,但是您遗漏了最重要的部分(强调我的):

14.3.2p1 模板非类型参数 [temp.arg.nontype]

模板参数的用于非类型,非模板模板参数应是以下之一:

  • ...

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


注意:在早期的草稿中,"其中id-expression是对象或函数的名称"不存在; DR 1570解决了这一问题- 这无疑使意图更加清晰.


引用类型的变量不是对象

你是完全正确的; 引用本身具有引用类型,并且只能在表达式的一部分时充当对象.

5p5 表达式 [expr]

如果表达式最初具有"引用T" 类型(8.3.2,8.5.3),则T在进行任何进一步分析之前调整类型.表达式指定由引用表示的对象或函数,表达式是左值或x值,具体取决于表达式.


值得注意的是,常量表达式("指定完整对象的地址......")必须是&id-expression或之一id-expression.

即使一个常数表达式,这不只是一个身份的表达,可能是指具有静态存储持续时间的对象,我们不能用它来"初始化"一个模板参数参比指针类型.

示例代码段

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
Run Code Online (Sandbox Code Playgroud)

注意:这也是我们不能将字符串文字用作模板参数的原因 ; 它们不是id表达式.