18

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

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

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

然而,需要在声明时(例如,在类定义中)初始化constexpr(AFAIK,无法从标准中找到引用)。

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

这就是我最终的结果:

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 成员(在类之外使用模板定义)?

4

2 回答 2

15

如果它可以帮助任何人,以下使用 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

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

于 2014-03-14T19:26:59.617 回答
14

我想你想要 9.4.2p3:

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

模板静态数据成员的定义是模板声明(14p1)。14.5.1.3p1 中给出的例子是:

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

但是,如上所述,其类内声明指定初始化程序constexpr static的or成员不应在其命名空间范围定义中具有初始化程序,因此语法变为:const static

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

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

于 2013-01-18T09:47:40.370 回答