c++ - 带有 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 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 版本 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;

这次第一个编译器说:

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& 类型?好吧,我可以按原样使用它...我知道它是一个引用,但与 native 引用没有区别,或者?

最佳答案

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

The syntactic restrictions for pointers, references, and pointers to members are awkward and prevent reasonable refactorings. For instance:

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

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

并进一步说:

The historical reason for the restriction was most likely that C++ previously did not have a sufficiently strong specification for constant expressions of pointer, reference, or pointer-to-member type. However, that is no longer the case. The status quo is that an implementation is required to evaluate such a template argument, but must then discard the result if it turns out to not be null.

In addition to the above, the restriction to entities with linkage is an artifact of exported templates, and could have been removed when the linkage restrictions on template type parameters were removed.

它会删除带有此限制的注释的这一部分:

unnamed lvalues, and named lvalues with no linkage

整条注释如下:

Temporaries, unnamed lvalues, and named lvalues with no linkage are not acceptable template-arguments when the corresponding template-parameter has reference type.

更新

本提案的修订版N4268adopted into the working draft at Urbana我们可以看到最新工作草案中的变化N4296 .新说明如下:

A temporary object is not an acceptable template-argument when the corresponding template-parameter has reference type

规范部分是 14.3.2 (temp.arg.nontype) 段落 1 在这个提案中会说:

For a non-type template-parameter of reference or pointer type, the value of the constant expression shall not refer to (or for a pointer type, shall not be the address of):

  • a subobject (1.8),
  • a temporary object (12.2),
  • a string literal (2.14.5),
  • the result of a typeid expression (5.2.8), or
  • a predefined func variable (8.4.1).

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

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

关于c++ - 带有 constexpr 函数失败的模板实例化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27410465/

相关文章:

c++ - 模板尾部结构凸起 "incomplete type in nested name specifier"

c++ - 将整数转换为字节数组时获取奇数字符

c++ - 为什么 std::sort 不通过引用接受比较器?

c - 有什么干净的方法可以让 OpenMP pragmas 与宏一起工作?

c++ - 指向可以更改指针的对象的智能指针

c++ - 尝试验证 C++ 中的输入

c++ - G++ CAS 的奇怪行为

c++ 11递归类模板到复杂错误

c++ - 类模板,allocator_traits

c++ - 将多维数组传递给 function_111