作为非类型模板参数的引用

Reference as a non-type template argument

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

int obj = 42;
int& ref = obj;

template <int& param> class X {};

int main()
{
    X<obj> x1;  // OK
    X<ref> x2;  // error
}

Live example


CLang 说:

source_file.cpp:9:7: error: non-type template argument of reference type 'int &' is not an object

其他人也有类似的抱怨。


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

14.3.2/1 A template-argument for a non-type, non-template template-parameter shall be one of:

...

  • a constant expression (5.19) that designates the address of an object with static storage duration and external or internal linkage ... expressed (ignoring parentheses) as & id-expression, except that the & ... shall be omitted if the corresponding template-parameter is a reference

...

什么是常量表达式:

5.19/2 A conditional-expression is a core constant expression unless it involves one of the following as a potentially evaluated subexpression (3.2)...

...

  • an id-expression that refers to a variable or data member of reference type unless the reference has a preceding initialization, initialized with a constant expression

...


据我所知,X<ref> 中的 ref 是一个 id-expression,它引用了一个引用类型的变量。这个变量有一个前置初始化,用表达式 obj 初始化。我相信 obj 是一个常量表达式,如果不是,那么 X<obj> 也不应该编译。

简介

reference 的名称是 id-expression 是正确的说法; id-expression 不引用引用所引用的任何内容,而是引用本身。

 int    a = 0;
 int& ref = a; // "ref" is an id-expression, referring to `ref` - not `a`

标准 (N4140)

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

14.3.2p1 Template non-type arguments [temp.arg.nontype]

A template-argument for a non-type, non-template template-parameter shall be one of:

  • ...

  • a constant expression (5.19) that designates the address of a complete object with static sturage duration and external or internal linkage or a function with external or internal linkage, including function templates and function template-ids but excluding non-static class members, expressed (ignoring parentheses) as & id-expression, where id-expression is the name of an object or function, except that the & may be omitted if the name refers to a function or array and shall be omitted if the corresponding template-parameter is a reference; ...


注意:在早期的草稿中“其中id-expression是对象或函数的名称”不是'现在;它由 DR 1570 解决 - 这无疑使意图更加清晰。


引用类型的变量不是对象?

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

5p5 Expressions [expr]

If an expression initially has the type "reference to T" (8.3.2, 8.5.3), the type is adjusted to T prior to any further analysis. The expression designates the object or function denoted by the reference, and the expression is an lvalue or an xvalue, depending on the expression.


详细说明

非常重要的是要注意常量表达式“指定一个完整对象的地址...”)必须是 &id-expressionid-expression.

之一

即使 constant-expression,它不仅仅是一个 id-expression,也可能引用具有静态存储的对象持续时间,我们不能用它来“初始化” 模板参数 of reference- or 指针类型.

示例代码段

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作为[=]的原因46=]模板参数;它们不是 id-expressions.