非类型引用参数/参数

时间:2015-10-23 11:46:52

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

为什么非类型引用的模板参数不能是另一个引用(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),用于指定具有静态存储持续时间和外部或内部链接的对象的地址,或具有外部或内部链接的函数,包括函数模板   和函数template-ids但不包括非静态类成员,表示(忽略括号)为&amp; id-expression,除了&amp;如果名称引用函数或数组,则可以省略,如果相应的模板参数是引用,则应省略;或者......

     

[。4]

     

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

     

[5]

     

- 对于对象类型引用的非类型模板参数,不适用任何转换。提到的类型   通过引用,可以比模板参数的(否则相同)类型更加cv限定。   template-parameter直接绑定到template-argument,它应该是一个   左值。

不应该将

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

2 个答案:

答案 0 :(得分:3)

这与我之前在评论中链接的上一个问题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),用于指定具有静态存储持续时间的完整对象的地址   和外部或内部联系或具有外部或内部联系的功能,包括功能   模板和函数template-id但不包括非静态类成员,表示(忽略括号)   作为&amp; id-expression,其中id-expression是对象或函数的名称,除了   &安培;如果名称引用函数或数组,则可以省略,如果相应则应省略   template-parameter是一个引用;或
  •   
     

[...]

为:

  

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

     
      
  • 子对象(1.8),

  •   
  • 临时对象(12.2),

  •   
  • 字符串文字(2.13.5),

  •   
  • typeid表达式(5.2.8)或

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

  •   

和节5.20的更改常量表达式[expr.const] / p4在转换的常量表达式上有以下段落,它开始于:

  

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

特别增加了这一点:

  

[...]并且引用绑定(如果有的话)直接绑定[...]

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

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

答案 1 :(得分:2)

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

  

非类型模板参数 template-argument 应为a   转换为模板参数 类型的常量表达式(5.20)。对于参考[...]类型的非类型模板参数,   常数表达式的值不应指[...]:[......不适用的情况......]

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

  

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

显然,参考文献直接绑定。在此上下文中是xp常量表达式吗?

[expr.const] / 5:

  

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

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

  

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

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

  

条件表达式 e核心常量表达式,除非e [...]的评估将评估以下表达式之一:

     

- 引用其变量或数据成员的 id-expression   引用类型,除非引用具有先前的初始化和   任

     
      
  • 使用常量表达式初始化
  •   

x,因为p的初始化程序是一个常量表达式(就像它是int&的有效模板参数一样),因此p作为模板参数也是一个不变的表达。

请注意,Clang as of version 3.6会将您的代码段编译好。