C++ constexpr函数失败的模板实例化

C++ constexpr函数失败的模板实例化,c++,templates,c++11,language-lawyer,constexpr,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 = {33,44}; 我还声明了一个返回对p的引用的函数: constexpr const P &h() { return p; } 然后尝试在以下方面使用这些

我有一个模板类
C
,它有一个非类型但引用模板参数的类型
p

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

template <const P &x>
class C {
public:
  const int &f() { return x.x; }
};
P p = {33,44};
我还声明了一个返回对
p
的引用的函数:

constexpr const P &h() { return p; }
然后尝试在以下方面使用这些:

C<p> o;    // line 1
C<h()> oo; // line 2
为什么会这样?我在规范中找不到反对它的论据。我不确定这是否与中的问题完全相同,在中讨论的是嵌套实例化的实例化点。这更像是一个类型问题,但哪一个呢?我的函数
h()
返回对定义良好类型(
const P&
)的定义良好的变量的引用。我期望一些内联会发生,并给出正确的结果,但事实并非如此。你能告诉我为什么吗

将函数声明为内联不会改变任何问题

使用
Apple LLVM版本6.0(clang-600.0.56)(基于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;
第二点:

error: could not convert template argument 'pp' to 'const P &'
pp
不是对象?
pp
不是类型
const p&
?我可以按原样使用它……我知道它是引用,但与本机引用无法区分,或者在

C<h()> oo;
coo;
§14.3.2/4引入:

[注:临时变量,未命名左值和未链接的命名左值是不可接受的模板- 对应模板参数具有引用类型时的参数


(emphasis mine)

这项限制似乎受制于以下提案,仍在试图确定该提案的地位。该提案说:

指向的指针、引用和指针的语法限制 成员很笨拙,无法进行合理的重构。例如:

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

constexpr int *p() { return &n; }
A<p()> b; // error
模板结构A{};
int n;
A;//好的
constexpr int*p(){return&n;}
A b;//错误
并进一步表示:

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

除上述内容外,对具有链接的实体的限制如下: 导出模板的工件,在 已删除对模板类型参数的链接限制

它将删除注释中有以下限制的这一部分:

未命名的左值和未链接的命名左值

全文如下:

临时值、未命名左值和未链接的命名左值是 当相应的 模板参数具有引用类型

更新

这项提案的修订版本是,我们可以在最新的工作草案中看到变化

在以下情况下,临时对象不是可接受的模板参数: 对应的模板参数具有引用类型

规范性章节为
14.3.2
(临时参数非类型)段落
1
,该段落与本提案一起应说明:

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

  • 子对象(1.8)
  • 临时物体(12.2)
  • 字符串文字(2.14.5)
  • typeid表达式(5.2.8)的结果,或
  • 预定义的func变量(8.4.1)
我们可以在最新的标准草案中找到这种新的措辞


看起来这个更改实际上已经在
clanghead
中实现了,请使用
-std=c++1z
标志查看您的代码工作。这意味着该更改应该是c++17的一部分,假设没有后续更改逆转或更改它。

FWIW GCC 4.9.1还说
错误:'h()“”不是类型“const P&”的有效模板参数,因为它不是具有外部链接的对象<代码> >代码> P<代码>代码> >代码> P> >外部链接,这与C>P><代码>有关,为什么不<代码> C>代码>?我认为问题是,自从C++中引入了CONTXPRPR以来,没有对非类型模板参数进行过检查。谢谢。我不确定文档(非常有趣!)回答我的问题。如果我理解它涉及类型等价/相等(我关心的是哪一个,但不是这个)…您可以使用
clanghead
查看我更新答案中的实例,这样不仅建议被接受,而且我们还可以看到它也可以工作。标准中的注释不只是脚注或示例。有一个问题专门针对这一点。这是一个很好的发现。我在阅读本节时遗漏了未修改的左值!!!但是现在,为什么连内联函数
h
都不能产生期望的结果呢?你不认为所有需要的东西都在编译器的掌控之下吗(没有陷阱)?这可能就是为什么我没有被未修改的左值所唤醒…抱歉,杰弗里,沙菲克的答案有很好的文件证明。哥伦布,当然它不是规范性的,但是有注释来澄清规范性部分,所以任何人都可以理解之前的陈述是这样说的。N4268,链接文件的修订版,在Urbana被采用。@T.C.谢谢你,自从我回答了你的问题后,我刚刚恢复了在线
C<h()> oo;
template<int *p> struct A {};
int n;
A<&n> a; // ok

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