Enr*_*lis 24 c++ static variable-declaration language-lawyer
Scott Meyers 在 Effective Modern C++, Item 30 page 210 中写道,有
无需
static const
在类中定义完整的数据成员;仅声明就足够了,
那么示例代码是
class Widget {
public:
static const std::size_t MinVals = 28; // MinVals' declaration;
...
};
... // no defn. for MinVals
std::vector<int> widgetData;
widgetData.reserve(Widget::MinVals); // use of MinVals
Run Code Online (Sandbox Code Playgroud)
我确信这static const std::size_t MinVals = 28;
既是声明又是定义,因为它给了一个值MinVals
,但评论似乎声称这只是一个声明;第二条评论实际上声称没有定义。代码后面的文字,确实是读
MinVals
缺乏定义。
这证实这static const std::size_t MinVals = 28;
不是一个定义,所以我有点困惑。
cppreference对我帮助不大(我的粗斜体):
如果声明
static
了整型或枚举类型的数据成员(而不是),则可以使用初始化器对其进行初始化,其中每个表达式都是常量表达式,就在类定义中:const
volatile
Run Code Online (Sandbox Code Playgroud)struct X { const static int n = 1; const static int m{2}; // since C++11 const static int k; }; const int X::k = 3;
但是类中的前两行在我看来是定义。
以下 cppreference 示例也是如此:
Run Code Online (Sandbox Code Playgroud)struct X { static const int n = 1; static constexpr int m = 4; }; const int *p = &X::n, *q = &X::m; // X::n and X::m are odr-used const int X::n; // … so a definition is necessary constexpr int X::m; // … (except for X::m in C++17)
static const int n = 1;
根据倒数第二个评论,我会说的是一个定义,但它不是。
Pio*_*cki 17
无需在类中定义完整的静态常量数据成员;仅声明就足够了,
仅当该对象未使用ODR时,单独声明就足够了,也就是说,如果数据成员未在需要其地址存在的上下文中使用(例如绑定到引用或应用运算符&
)。初始化程序的存在不等于定义。
在书中的示例中,很明显它MinVals
不是 ODR 使用的,即编译器可以直接使用其值,而无需在内存中创建对象,因此语句:
widgetData.reserve(Widget::MinVals);
Run Code Online (Sandbox Code Playgroud)
变成:
widgetData.reserve(28);
Run Code Online (Sandbox Code Playgroud)
但是,如果在任何其他地方MinVals
使用 ODR,就会使程序格式错误。
cppreference 中的所有其他示例都清楚地表明了何时使用 ODR 并且需要定义,何时不需要:
struct X
{
const static int n = 1;
const static int m{2}; // since C++11
const static int k;
};
const int X::k = 3;
Run Code Online (Sandbox Code Playgroud)
n
和m
是带有初始值设定项的声明。尝试获取n
或m
应该失败的地址。
struct X {
static const int n = 1;
static constexpr int m = 4;
};
const int *p = &X::n, *q = &X::m;
const int X::n;
constexpr int X::m;
Run Code Online (Sandbox Code Playgroud)
表达式&X::n
和&X::m
计数作为ODR用途的n
和m
分别(即,地址被请求)。对于constexpr
静态数据成员,在 C++17 之前需要定义。从 C++17 开始,static constexpr
数据成员是隐式的inline
,这意味着不需要类外定义,因为它们本身就是定义。