使用constexpr函数失败的模板实例化

时间:2014-12-10 20:44:15

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

我的模板类C对类型P有一个非类型但参考模板参数:

class P {
public:
  int x;
  int y;
};

template <const P &x>
class C {
public:
  const int &f() { return x.x; }
};

我声明了P类型的全局变量:

P p = {33,44};

我还声明了一个返回p的引用的函数:

constexpr const P &h() { return p; }

然后尝试使用以下内容:

C<p> o;    // line 1
C<h()> oo; // line 2

当然我对第一次实例化没有问题,但第二次实例化没有问题。我的编译器抱怨:

error: non-type template argument does not refer to any declaration

为什么会这样?我无法在常规中找到反对它的论据。我不确定它与Calling constexpr in default template argument中的问题完全相同,其中讨论的内容是关于嵌套实例化的实例化。这是一个类型问题,但哪一个?我的函数h()返回对定义良好的类型(const P &)的定义良好的变量的引用。我预计会有一些内联会给出正确的结果但事实并非如此。你能告诉我为什么吗?

将该函数声明为内联不会改变问题。

使用Apple LLVM version 6.0 (clang-600.0.56) (based on LLVM 3.5svn进行了实验。我还尝试使用g++-mp-4.8 (MacPorts gcc48 4.8.3_2) 4.8.3,错误报告为:

'h()' is not a valid template argument for type 'const P&' because it is not an object with external linkage

看起来我对h()的调用(这是一个constexpr所以编译时可计算的)不是这样的......


我忘了说如果我们尝试使用这样的另一个引用问题是相同的:

const P &pp = p;

然后

C<pp> oo;

这次第一个编译器说:

non-type template argument of reference type 'const P &' is not an object

和第二个:

error: could not convert template argument 'pp' to 'const P &'

pp不是对象? pp不属于const P&类型?好吧,我可以使用它,因为它...我知道它是一个参考但与原生参考无法区分,或者?

2 个答案:

答案 0 :(得分:15)

看来此限制受以下提案Allow constant evaluation for all non-type template arguments的约束,仍在尝试确定此提案的状态。它说:

  

指针,引用和指针的语法限制   成员很尴尬,阻止合理的重构。例如:

template<int *p> struct A {};
int n;
A<&n> a; // ok

constexpr int *p() { return &n; }
A<p()> b; // error

并进一步说:

  

限制的历史原因很可能是C ++   以前没有足够强大的规格   指针,引用或指向成员类型的常量表达式。   但是,情况已不再如此。现状是一个   需要实现来评估这样的模板参数,但是   如果结果不为空,则必须丢弃结果。

     

除上述内容外,对具有链接的实体的限制是   导出模板的工件,可能在删除时删除   删除了对模板类型参数的链接限制。

它会删除此部分的注释:

  

未命名的左值,以及没有链接的命名左值

整个笔记都写着:

  

Temporaries,未命名的左值和没有链接的命名左值   相应的时候不能接受模板参数   template-parameter具有引用类型。

更新

此提案的修订版N4268adopted into the working draft at Urbana,我们可以看到最新工作草案N4296中的更改。新的说明如下:

  

当临时对象不是可接受的模板参数时   相应的模板参数具有引用类型

规范部分是14.3.2 (temp.arg.nontype)段落 1,该提案会说:

  

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

     
      
  • 子对象(1.8),
  •   
  • 临时对象(12.2),
  •   
  • 字符串文字(2.14.5),
  •   
  • typeid表达式(5.2.8)或
  • 的结果   
  • 预定义的 func 变量(8.4.1)。
  •   

我们可以在最新的标准草案N4296中找到这一新措辞。

看起来这个更改实际上已在clang HEAD中实现,使用-std=c++1z标记查看您的代码是否正常live。这意味着更改应该是C ++ 17的一部分,假设后续更改没有反转或改变它。

答案 1 :(得分:5)

的情况下
C<h()> oo;

§14.3.2/ 4开始了:

  

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

(强调我的)