关于静态常量数据模因的声明和定义的混淆
Confusion about declaration and definition of static const data memebers
斯科特·迈耶斯(Scott Meyers(在《有效的现代C++》(第30项,第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
我确信static const std::size_t MinVals = 28;
是声明,也是一个定义,因为它赋予MinVals
一个值,但评论似乎声称这只是一个声明;第二个评论实际上声称没有定义。代码后面的文字,确实读
MinVals
缺乏定义。
这证实了static const std::size_t MinVals = 28;
不是一个定义,所以我有点困惑。
CPP偏好对我没有多大帮助(我的粗斜体(:
如果整型或枚举类型的
static
数据成员被声明为const
(而不是volatile
(,则可以使用初始值设定项对其进行初始化,其中每个表达式都是一个常量表达式,就在类定义中:struct X { const static int n = 1; const static int m{2}; // since C++11 const static int k; }; const int X::k = 3;
但类中的前两行对我来说看起来是定义的。
以下关于 cpp 首选项的示例也是如此:
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;
是一个定义,但事实并非如此,基于倒数第二条评论。
不需要在类中定义完整的静态常量数据成员;仅声明就足够了,
仅当该对象未使用 ODR 时,即,如果数据成员未在需要其地址存在的上下文中使用(如绑定到引用或应用运算符&
(,则仅声明就足够了。初始值设定项的存在不等于定义。
在本书的示例中,很明显MinVals
没有使用 ODR,即编译器可以直接使用其值,而无需在内存中创建对象,因此语句:
widgetData.reserve(Widget::MinVals);
成为:
widgetData.reserve(28);
但是,如果在任何其他位置使用ODRMinVals
,这将使程序格式不正确。
来自 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
的,这意味着不需要类外定义,因为它们本身就是定义。
查看此标准草案,您的示例似乎属于灰色区域。虽然没有明确提及以下行:
static const std::size_t MinVals = 28;
给出一个非常相似的例子:
6.1 声明和定义
...
2 声明是一个定义,除非
...
2.3 — 它在类定义
中声明了一个非内联静态数据成员...
示例:除以下一项外,其他所有定义都是:
int a;//定义一个
外部常量 int c = 1;//定义 c
...
第二个示例与您的代码很接近,但在具有extern
限定符方面存在显着差异。另外,请注意,上述声明(默认情况下(也是一个定义,除非列出的条件之一适用;我想说(虽然我不是语言律师(在你的案件中,这些条件都没有完全满足,所以你的声明也是一个定义。
注意:链接的文档只是一个标准草案;请务必阅读其第一页底部的"免责声明"!
摘自标准章节"12.2.3.2 静态数据成员":
如果在程序中使用 odr 时,仍应在命名空间作用域中定义该成员,并且命名空间作用域定义不应包含初始值设定项。
通过使用它,应对其进行定义。
- 如何在C++中声明/定义相互依赖的模板?
- C++概念是否允许我的类在声明/定义中指定它满足某些概念?
- MSVC:无法识别的模板声明/定义(使用 Clang/GCC 编译)
- 我可以重用同一个模板来声明/定义多个东西吗(而不复制模板代码)
- C++-模板类中模板函数的单独声明/定义
- 在C 中使用继承时,请避免使用未缴纳的函数声明/定义
- C++模板能否确定所声明/定义的实例是否为常量
- 如何在程序中声明/定义一次并在两个类中使用映射列表
- LBNF,C函数声明/定义,减少冲突
- 声明/定义返回具有自动返回类型的 valarray 的函数时的隔离错误
- #用{}和声明定义混淆
- CTOR 声明/定义中接受的 const 限定符(LLVM 错误?)
- C++ 从一个源文件中声明/定义的变量从另一个源文件访问另一个源文件
- 无法识别的模板声明/定义
- 只在.cpp中声明/定义静态方法可以吗
- 声明/定义自定义类定制对象的正确方法
- typedef声明定义未命名类时链接失败
- 用visual c++实现COM对象的声明/定义和实例化
- VS 2012中的显式模板声明/定义
- 命名空间内的友元函数声明/定义