【问题标题】:Using constexpr array as a template non-type argument (C++14)使用 constexpr 数组作为模板非类型参数 (C++14)
【发布时间】:2017-09-17 14:15:23
【问题描述】:
#include <iostream>

using namespace std;

template<const int arr[]>
struct S {
    static constexpr int value = arr[0];
};

constexpr int arr[] = { 5 };

int main() {
    cout << S<arr>::value << endl;
}

此程序编译良好并使用 gcc 5.1 及更高版本打印 5,但 MSVC 19.10.25019 给出以下错误:

错误 C2975:“S”:“arr”的模板参数无效,应为 编译时常量表达式错误 C2131:表达式没有 计算为常数

这个程序根据 C++14 标准是有效的,还是 gcc 在这里太宽松了?

【问题讨论】:

  • 郑重声明,Clang 也接受此代码。
  • 如果将arr[] 替换为arr[1],MSVC 会说什么?
  • @jwimberley 它不会改变任何东西。

标签: c++ gcc visual-c++ c++14 constexpr


【解决方案1】:

就我所见,该程序的格式良好。

根据 [temp.param]/8,模板参数的类型实际上是const int*,而不是const int[]

将“T 数组”或“函数返回T”类型的非类型模板参数调整为类型 “指向T的指针”或“指向返回T的函数的指针”。

根据[temp.arg.nontype]/1,我们可以使用具有静态存储持续时间和外部链接的完整数组对象的名称作为此类模板参数的参数:

用于非类型、非模板的 模板参数 模板参数 应为以下之一:

...

— 一个常量表达式 (5.19),它指定具有静态存储持续时间和外部或内部链接的完整对象的地址或具有外部或内部链接的函数,包括函数 模板和函数template-ids,但不包括非静态类成员,表示(忽略括号)为&amp;id-expression,其中id-expression 是对象或函数的名称,除了 &amp; 如果名称指的是函数或数组,则可以省略,如果对应的名称应省略 模板参数是一个参考...

arr 是一个常量表达式,尽管 MSVC 认为它不是。它是根据 [expr.const]/2 的核心常量表达式,因为它不包含任何禁止的评估,并且它是一个常量表达式,因为它指向具有静态存储持续时间的对象([expr.const]/4)。

因为模板参数是指一个具有静态存储时长的数组,所以数组的边界在模板实例化时是已知的。因此,它可以验证对 arr[0] 的访问是合法的核心常量表达式,因为它具有明确定义的行为,并且属于 [expr.const]/2 中允许的左值到右值转换的类别:

... 一个整数或枚举类型的非易失性左值,它引用一个非易失性 const 对象 前面的初始化,用常量表达式初始化

【讨论】:

  • 从技术上讲,它违反了 [temp.arg.nontype]/2.1:“对于 ... 指针类型的非类型模板参数,常量表达式的值不应 ...一个子对象的地址”。指针指向数组的第一个元素,它是一个子对象。但这是当前措辞的一个已知问题。参见 CWG 2043。
猜你喜欢
  • 1970-01-01
  • 2018-06-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-16
  • 2017-02-09
相关资源
最近更新 更多