【问题标题】:Reference as a non-type template argument作为非类型模板参数的引用
【发布时间】:2015-04-24 03:03:26
【问题描述】:

下面的示例尝试使用引用类型的变量作为非类型模板形参(其本身为引用类型)的实参。 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: 错误:引用类型“int &”的非类型模板参数不是对象

其他人也以类似的方式抱怨。


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

14.3.2/1 用于非类型、非模板的 template-argument template-parameter 应为以下之一:

...

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

...

现在什么是常量表达式:

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

...

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

...


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

  • 那么我错过了什么?
  • 标准中的哪个条款使X&lt;ref&gt;无效,而X&lt;obj&gt;有效?

【问题讨论】:

  • int x = 0; constexpr int&amp; r = x; template &lt;int* param&gt; class X {}; X&lt;&amp;r&gt;{}; 发生了类似的问题据我所知,编译器拒绝这些情况,因为它们“不遵循”表达式所指的内容。我怀疑他们直接使用出现在 id-expression 中的错误名称来为(类)模板专业化创建一个名称(用于链接目的)(或者标准打算允许这样一个简单的实现)。
  • @dyp 查看n4198
  • @remyabel 谢谢,我已经在关于指针的相关问题中链接到N4268(参见-->)
  • g++ 实际上对这个问题给出了合理的解释。

标签: c++ c++11 language-lawyer


【解决方案1】:

简介

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

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

标准 (N4140)

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

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

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

  • ...

  • 一个常量表达式 (5.19),它指定一个完整对象的地址,该对象具有静态持续时间和外部或内部链接,或具有外部或内部链接的函数,包括函数模板和函数 template-ids,但不包括非静态类成员,表示(忽略括号)为&amp;id-expressionwhere id-expression em> 是一个对象或函数的名称,除非&amp; 指的是一个函数或数组,如果是对应的模板参数,则应该省略是参考; ...


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


引用类型的变量不是对象

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

5p5 表达式 [expr]

如果表达式最初的类型为“引用T”(8.3.2、8.5.3),则在进行任何进一步分析之前将该类型调整为T。表达式指定引用所指的对象或函数,表达式是左值还是x值,具体取决于表达式。


细化

非常重要的是要注意常量表达式“表示一个完整对象的地址...”)必须是&amp;id-expression之一,或id-expression

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

示例片段

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 表达式

【讨论】:

  • 我的 C++11 副本(最终草案,忘记了确切的 N 数)没有说“其中 id-expression 是对象或函数的名称”。 C++14 (N3936) 可以;我错过了。
  • @IgorTandetnik 我将更新我的答案以反映它在 C++11 中不存在,尽管在以前的标准中含义是相同的;它仍然使用id-expression
  • 我不同意引用并不表示对象的地址。请参阅DR 1570(添加了相关措辞)。如果您的声明属实,则无需进行任何更改,DR 1570 将是 NAD。我想说未能禁止 X&lt;ref&gt; 是 C++11 中的一个缺陷,在 C++14 中更正。
  • @IgorTandetnik a "revised" 措辞并不意味着意图不存在,它只是意味着它应该更精确 - 这是我的看法;您对如何使我的答案的措辞更清晰(更准确)有任何建议吗?
  • @IgorTandetnik 查看更新(编辑)的答案,我没有尝试自己澄清事情,而是冒昧地添加了指向相关 DR 的链接。你怎么看?
猜你喜欢
  • 1970-01-01
  • 2019-04-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-20
  • 2013-03-30
  • 2015-06-03
  • 2021-03-18
相关资源
最近更新 更多