【问题标题】:Non-type reference parameter/argument非类型引用参数/参数
【发布时间】:2015-10-23 11:46:52
【问题描述】:

为什么非类型引用的模板参数不能是另一个引用(g++ 4.8.1):

template <int& N> void test() { }

int x = 5;
int& p = x;

int main(){
    test<x>(); //compiles fine
    test<p>(); //error: could not convert template argument 'p' to 'int&'|  
}

我看不出标准 p 违反了什么规定,这些似乎是最相关的部分 (N3337):

[14.3.2] [.1] 非类型、非模板模板参数的模板参数应为以下之一:

——对于整数或枚举类型的非类型模板参数,模板参数类型的转换常量表达式(5.19);或

——非类型模板参数的名称;或

— 一个常量表达式 (5.19),它指定具有静态存储持续时间和外部或内部链接的对象或具有外部或内部链接的函数的地址,包括函数模板 和函数模板 ID,但不包括非静态类成员,表示(忽略括号)为 & id 表达式,但如果名称指代函数或数组,则 & 可以省略,如果对应的模板参数则应省略是参考;或者.....

[.4]

[ 注意:临时、未命名的左值和没有链接的命名左值是不可接受的模板参数 当对应的模板参数有引用类型时。

[.5]

- 对于对象类型引用的非类型模板参数,不适用任何转换。引用的类型 to by reference 可能比模板参数的(否则相同)类型更符合 cv 要求。 模板参数直接绑定到模板参数,它应该是一个 左值。

p 应该被视为左值,不是吗?我唯一能想到的另一件事可能是缺少引用链接,但添加extern int&amp; p = x 也没有解决它。

【问题讨论】:

标签: c++ templates c++11 reference non-type


【解决方案1】:

这与我不久前在 cmets 中链接到的上一个问题 template instantiation with constexpr function failure 有关,尽管您的情况不同。

看起来该示例以前是不允许的,但通过提案Allow constant evaluation for all non-type template arguments 将支持添加到 C++1z 中,该提案的开头为:

指针、引用和指向的指针的句法限制 成员很尴尬,并阻止了合理的重构。 [...] 这 限制的历史原因很可能是 C++ 以前没有足够强的规范 指针、引用或指向成员类型的常量表达式。 然而,情况已不再如此。 [...]

似乎与您的案例相关的具体更改是对草案 C++ 标准部分 14.3.2 模板非类型参数 [temp.arg.nontype]/p1 的改写来自:

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

[...]

  • 一个常量表达式 (5.19),它指定具有静态存储持续时间的完整对象的地址 和外部或内部链接或具有外部或内部链接的功能,包括功能 模板和函数模板 ID,但不包括非静态类成员,表示(忽略括号) as & id-expression,其中 id-expression 是对象或函数的名称,除了 & 如果名称指的是函数或数组,则可以省略,如果对应的名称应省略 模板参数是一个参考;或

[...]

到:

非类型模板参数的模板参数应是转换后的常量表达式 (5.20) 模板参数的类型。对于引用或指针类型的非类型模板参数, 常量表达式的值不应引用(或对于指针类型,不应是地址):

  • 子对象 (1.8),

  • 一个临时对象 (12.2),

  • 字符串字面量 (2.13.5),

  • typeid 表达式 (5.2.8) 的结果,或

  • 预定义的 func 变量 (8.4.1)。

5.20 常量表达式部分的更改 [expr.const]/p4 有以下关于转换常量表达式的段落,开头是:

类型 T 的已转换常量表达式是隐式转换为类型 T 的表达式,其中转换后的 表达式为常量表达式,隐式转换序列只包含

特别添加了这个:

[...] 以及引用绑定(如果有)直接绑定的地方 [...]

注意,clang 的当前头部版本以 C++1z 模式编译您的代码,see it live

N4268 的更新版本是应用的版本,clang C++1z implementation status section 表示本文是由 clang 3.6 支持的。此代码仅适用于 clang 3.6 及更高版本的 C++1z 模式。

【讨论】:

    【解决方案2】:

    使用 N4268(现在在 WD 中)引入的简化措辞,

    非类型模板参数的模板参数应该是 模板参数类型的转换常量表达式(5.20)。对于引用 […] 类型的非类型 template-parameter, 常量表达式的值不应引用 […]: […cases that don't apply…]

    “转换后的常量表达式”在[expr.const]/4中定义:

    T 类型的转换后的常量表达式 是一个表达式, 隐式转换为 T 类型,其中转换后的表达式是 常量表达式和隐式转换序列包含 只有 [...] 并且引用绑定(如果有)直接绑定。

    显然,引用直接绑定。 xp 在这种情况下是常量表达式吗?

    [expr.const]/5:

    一个常量表达式要么是一个glvalue核心常量表达式 其值指的是一个实体,该实体是一个允许的结果 常量表达式(定义如下),或 […]

    常量表达式的允许结果在下一段中定义为

    ...具有静态存储持续时间的对象,不是临时对象或 [...]

    xp 确实引用了具有静态存储持续时间的对象,但它们是给定上下文中的核心常量表达式吗?答案是肯定的:只要表达式不检查它们的 (或 p 所指的对象的值),但事实并非如此,一切都很好,即使对于 @987654329 @:

    条件表达式e核心常量表达式,除非e […] 的求值将求出以下表达式之一:

    ——一个id-expression,指的是一个变量或数据成员 引用类型,除非该引用具有前面的初始化和 要么

    • 用常量表达式初始化

    x 作为p 的初始值设定项是一个常量表达式(就像它是int&amp; 的有效模板参数一样),因此p 作为模板参数也是一个常量表达式。

    请注意,Clang as of version 3.6 可以很好地编译您的 sn-p。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-04-24
      • 1970-01-01
      • 2015-06-03
      • 1970-01-01
      • 2014-05-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多