【问题标题】:Proper initialization of static constexpr array in class template?在类模板中正确初始化静态 constexpr 数组?
【发布时间】:2013-01-18 09:34:22
【问题描述】:

由于标准的措辞,C++ 中的静态类成员给我带来了一些困惑:

9.4.2 静态数据成员 [class.static.data]

静态数据成员在其类定义中的声明不是定义...

但是,constexpr 需要在其声明(例如,在类定义中)进行初始化(AFAIK,无法从标准中找到引用)。

由于对 constexpr 的限制,我实际上忘记了在类之外定义静态成员的必要性,直到我尝试访问静态 constexpr 数组。 This related question 提供了定义数组成员的正确方法,但我对类模板中此定义的含义感兴趣。

这就是我最终的结果:

template<typename T>
class MyClass
{
private:
  static constexpr std::size_t _lut[256] = { /* ... */ };
  T _data;

public:
  static constexpr std::size_t GetValue(std::size_t n) noexcept
  {
    return _lut[n & 255];
  }

  // ...
};

template<typename T>
constexpr std::size_t MyClass<T>::_lut[256];

这是正确的语法吗? 特别是在定义中使用模板感觉很尴尬,但 GCC 似乎正确地链接了所有内容。

作为后续问题,是否应该类似地定义非数组静态 constexpr 成员(在类外使用模板定义)?

【问题讨论】:

  • 这里有一件令人困惑的事情:类中的声明不是定义,而是有一个初始化程序。类外的声明没有初始化器,而是一个定义。
  • 这令人困惑(至少恕我直言),但 constexpr 规范要求初始化程序位于声明点,而不是定义点。大概这与根据 ODR 处理常量表达式的方式有关(即 constexpr 成员不需要定义,只要它仅用于编译时常量表达式 - 请参阅 ecatmur 给出的答案) .

标签: c++ templates c++11 static-members constexpr


【解决方案1】:

如果它可以帮助任何人,以下对我使用 constexpr 的 GCC 4.7 有用:

template<class T> class X {
  constexpr static int s = 0;
};
template<class T> constexpr int X<T>::s; // link error if this line is omitted

我没有声称这是否“正确”。我会把它留给那些更有资格的人。

【讨论】:

  • 我需要一个 constexpr 结构。
【解决方案2】:

我认为你想要 9.4.2p3:

如果非易失性const static 数据成员是整数或枚举类型,它在类定义中的声明可以指定一个brace-or-equal-initializer,其中每个初始化器-clause 是一个 assignment-expression 是一个常量表达式 (5.19)。可以在类定义中使用 constexpr 说明符声明文字类型的静态数据成员;如果是这样,它的声明应指定一个 brace-or-equal-initializer,其中作为 assignment-expression 的每个 initializer-clause 都是一个常量表达式。 [...] 如果该成员在程序中被 odr-used (3.2) 并且命名空间范围定义不应包含 initializer,则该成员仍应在命名空间范围内定义。

template 静态数据成员的定义是 template-declaration (14p1)。 14.5.1.3p1中给出的例子是:

template<class T> class X {
  static T s;
};
template<class T> T X<T>::s = 0;

但是,如上所述,constexpr staticconst static 成员的类内声明指定了 initializer 不应在其命名空间范围定义中具有 initializer,因此语法变为:

template<class T> class X {
  const static T s = 0;
};
template<class T> T X<T>::s;

与非数组(即整数或枚举)静态 constexpr 数据成员的区别在于它在左值到右值转换中的使用不是 odr-use;如果获取它的地址或形成对它的 const 引用,您只需要定义它。

【讨论】:

  • 谢谢!编辑有一些有用的补充! :) 关于非数组成员,你是说因为它是一个 constexpr 成员实际上不必定义(只要它确实是一个常量表达式,并且在这样的上下文中使用)?
  • @monkey_05_06 精确; 3.2p3 表示变量x 的odr 使用发生除非 x“满足出现在常量表达式中的要求”,即它是整数const 或文字constexpr
  • 如果省略template&lt;class T&gt; T X&lt;T&gt;::s;,最好提供一个无法编译的示例,ODR 烧我的大脑,例如起点:stackoverflow.com/a/63613408/895245
猜你喜欢
  • 1970-01-01
  • 2021-08-07
  • 1970-01-01
  • 1970-01-01
  • 2013-07-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-29
相关资源
最近更新 更多