static const 数据成员的声明和定义的困惑
Confusion about declaration and definition of static const data memebers
Scott Meyers 在 Effective Modern C++(第 30 页第 210 页)中写道
no need to define integral static const
data members in classes; declarations alone suffice,
则示例代码为
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
我确信 static const std::size_t MinVals = 28;
是声明 也是 的定义,因为它给 MinVals
,但评论似乎声称这只是一个声明;第二条评论实际上声称没有定义。代码后的文字确实是
MinVals
lacks a definition.
这证实 static const std::size_t MinVals = 28;
不是定义,所以我有点困惑。
cppreference 对我帮助不大(我的粗斜体):
If a static
data member of integral or enumeration type is declared const
(and not volatile
), it can be initialized with an initializer in which every expression is a constant expression, right inside the class definition:
struct X
{
const static int n = 1;
const static int m{2}; // since C++11
const static int k;
};
const int X::k = 3;
但 class 中的前两行对我来说是定义。
以下关于 cppreference 的示例也是如此:
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;
是一个定义,但根据倒数第二条评论,它不是。
查看此 Draft Standard,您的示例似乎落入了灰色区域。虽然没有明确提及以下行:
static const std::size_t MinVals = 28;
给出的例子非常相似:
6.1 Declarations and definitions
...
2 A declaration is a definition unless
...
2.3 — it declares a non-inline static data member in a class definition
...
Example: All but one of the following are definitions:
int a; // defines a
extern const int c = 1; // defines c
...
第二个示例与您的代码 接近 ,但在 extern
限定符方面有显着差异。另外,请注意,除非列出的条件之一适用,否则上面声明的声明(默认情况下)也是 definition;我会说(虽然我不是语言律师)在你的情况下none 完全,所以你的声明也是一个定义。
注意:链接文档只是一个草案标准;请务必阅读第一页底部给出的 'disclaimer'!
no need to define integral static const data members in classes; declarations alone suffice,
仅当该对象不是 ODR-used 时,声明本身就足够了,也就是说,如果数据成员未在需要其地址存在的上下文中使用(例如绑定到引用或应用运算符 &
).初始值设定项 的存在不 等于定义。
在书中的示例中,很明显 MinVals
未使用 ODR,即编译器可以直接使用它的值,而不必在内存中创建对象,因此声明:
widgetData.reserve(Widget::MinVals);
变成:
widgetData.reserve(28);
但是,如果在任何其他地方 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;
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;
表达式 &X::n
和 &X::m
分别算作 n
和 m
的 ODR 使用(即请求地址)。对于 constexpr
静态数据成员,在 C++17 之前需要定义。从 C++17 开始,static constexpr
数据成员隐含地 inline
,这意味着不需要超出 class 的定义,因为它们本身就是定义。
来自The Standard章节“12.2.3.2静态数据成员”:
The member shall still be defined in a namespace scope if it is odr-used in the program and the namespace scope definition shall not contain an initializer.
使用它,它应该被定义。
Scott Meyers 在 Effective Modern C++(第 30 页第 210 页)中写道
no need to define integral
static const
data members in classes; declarations alone suffice,
则示例代码为
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
我确信 static const std::size_t MinVals = 28;
是声明 也是 的定义,因为它给 MinVals
,但评论似乎声称这只是一个声明;第二条评论实际上声称没有定义。代码后的文字确实是
MinVals
lacks a definition.
这证实 static const std::size_t MinVals = 28;
不是定义,所以我有点困惑。
cppreference 对我帮助不大(我的粗斜体):
If a
static
data member of integral or enumeration type is declaredconst
(and notvolatile
), it can be initialized with an initializer in which every expression is a constant expression, right inside the class definition:struct X { const static int n = 1; const static int m{2}; // since C++11 const static int k; }; const int X::k = 3;
但 class 中的前两行对我来说是定义。
以下关于 cppreference 的示例也是如此:
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;
是一个定义,但根据倒数第二条评论,它不是。
查看此 Draft Standard,您的示例似乎落入了灰色区域。虽然没有明确提及以下行:
static const std::size_t MinVals = 28;
给出的例子非常相似:
6.1 Declarations and definitions
...
2 A declaration is a definition unless
...
2.3 — it declares a non-inline static data member in a class definition
...
Example: All but one of the following are definitions:
int a; // defines a
extern const int c = 1; // defines c
...
第二个示例与您的代码 接近 ,但在 extern
限定符方面有显着差异。另外,请注意,除非列出的条件之一适用,否则上面声明的声明(默认情况下)也是 definition;我会说(虽然我不是语言律师)在你的情况下none 完全,所以你的声明也是一个定义。
注意:链接文档只是一个草案标准;请务必阅读第一页底部给出的 'disclaimer'!
no need to define integral static const data members in classes; declarations alone suffice,
仅当该对象不是 ODR-used 时,声明本身就足够了,也就是说,如果数据成员未在需要其地址存在的上下文中使用(例如绑定到引用或应用运算符 &
).初始值设定项 的存在不 等于定义。
在书中的示例中,很明显 MinVals
未使用 ODR,即编译器可以直接使用它的值,而不必在内存中创建对象,因此声明:
widgetData.reserve(Widget::MinVals);
变成:
widgetData.reserve(28);
但是,如果在任何其他地方 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;
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;
表达式 &X::n
和 &X::m
分别算作 n
和 m
的 ODR 使用(即请求地址)。对于 constexpr
静态数据成员,在 C++17 之前需要定义。从 C++17 开始,static constexpr
数据成员隐含地 inline
,这意味着不需要超出 class 的定义,因为它们本身就是定义。
来自The Standard章节“12.2.3.2静态数据成员”:
The member shall still be defined in a namespace scope if it is odr-used in the program and the namespace scope definition shall not contain an initializer.
使用它,它应该被定义。