我的模板类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&
类型?好吧,我可以使用它,因为它...我知道它是一个参考但与原生参考无法区分,或者?
答案 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具有引用类型。
更新
此提案的修订版N4268为adopted 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具有引用类型时的参数。
(强调我的)