Jea*_*nès 18 c++ templates language-lawyer constexpr c++11
我有一个模板类C,它具有类型的非类型但引用模板参数P:
class P {
public:
int x;
int y;
};
template <const P &x>
class C {
public:
const int &f() { return x.x; }
};
Run Code Online (Sandbox Code Playgroud)
我声明了一个类型的全局变量P:
P p = {33,44};
Run Code Online (Sandbox Code Playgroud)
我还声明了一个返回引用的函数p:
constexpr const P &h() { return p; }
Run Code Online (Sandbox Code Playgroud)
然后尝试在下面使用这些:
C<p> o; // line 1
C<h()> oo; // line 2
Run Code Online (Sandbox Code Playgroud)
当然我对第一次实例化没有问题,但第二次实例化没有问题.我的编译器抱怨:
error: non-type template argument does not refer to any declaration
Run Code Online (Sandbox Code Playgroud)
为什么会这样?我无法在常规中找到反对它的论据.我不确定它是否与在默认模板参数中调用constexpr完全相同的问题,其中讨论是关于嵌套 instanciation的实例化点.这是一个类型问题,但哪一个?我的函数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
Run Code Online (Sandbox Code Playgroud)
看起来我的调用h()(这是一个constexpr编译时可编译的)并不像这样......
如果我们尝试使用另一个这样的引用,我忘了说问题是一样的:
const P &pp = p;
Run Code Online (Sandbox Code Playgroud)
然后
C<pp> oo;
Run Code Online (Sandbox Code Playgroud)
这次第一个编译器说:
non-type template argument of reference type 'const P &' is not an object
Run Code Online (Sandbox Code Playgroud)
第二个:
error: could not convert template argument 'pp' to 'const P &'
Run Code Online (Sandbox Code Playgroud)
pp不是一个对象?pp是不是类型const P&?好吧,我可以使用它,因为它...我知道它是一个参考,但与原生参考无法区分,或?
Sha*_*our 15
看起来此限制受以下建议的约束.允许对所有非类型模板参数进行持续评估,仍尝试确定此提议的状态.它说:
对指针,引用和成员指针的语法限制很尴尬,并阻止合理的重构.例如:
Run Code Online (Sandbox Code Playgroud)template<int *p> struct A {}; int n; A<&n> a; // ok constexpr int *p() { return &n; } A<p()> b; // error
并进一步说:
限制的历史原因很可能是C++以前对指针,引用或指向成员类型的常量表达式没有足够强的规范.但是,情况已不再如此.现状是需要一个实现来评估这样的模板参数,但是如果结果不是null,则必须丢弃结果.
除上述内容外,对具有链接的实体的限制是导出模板的工件,并且在删除模板类型参数的链接限制时可能已被删除.
并且它会删除此条款的这一部分:
未命名的左值,以及没有链接的命名左值
整个笔记如下:
当相应的模板参数具有引用类型时,临时值,未命名的左值和没有链接的命名左值是不可接受的模板参数.
更新
N4268提案的修订版被纳入了Urbana的工作草案,我们可以看到N4296最新工作草案的变化.新的说明如下:
当相应的template-parameter具有引用类型时,临时对象不是可接受的模板参数
规范部分是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标志.这意味着更改应该是C++ 17的一部分,假设后续更改没有反转或改变它.
如果是
C<h()> oo;
Run Code Online (Sandbox Code Playgroud)
§14.3.2/ 4踢:
[注意:当相应的template-parameter具有引用类型时,临时值,未命名的左值和没有链接的命名左值是不可接受的模板参数.
(强调我的)