关于C++中带初始化器的static const类成员为何仅为声明而非定义的疑问
兄弟,你的观察真的很敏锐——这确实是C++标准里一个容易让人绕晕的特殊情况,Bjarne的表述其实没毛病,只是标准给类内的非内联静态数据成员开了专门的例外规则,咱们一步步理清楚:
首先,《The C++ Programming Language》里提到的两个通用规则是没问题的:
- 任何指定了值的声明都是定义
- 带初始化器的声明总是定义
但这两条是通用场景下的规则,而C++标准针对类内的非内联静态数据成员,有明确的例外规定:
11.4.9.1/3:The declaration of a non-inline static data member in its class definition is not a definition
你给出的例子struct Foo { static int const x = 3; };正好命中这个例外:
- 它是类内的非内联静态数据成员
- 虽然带了初始化器,但根据标准,它依然只是一个声明,而非定义
那为什么允许在类内给它初始化呢?这是因为对于静态const整数/枚举类型的成员,C++允许在类内提供初始化器,让它可以作为常量表达式被使用(比如用作数组大小、模板参数之类的场景),但这个初始化行为并不会把它变成一个定义。
什么时候才需要这个成员的定义呢?如果你的代码中需要获取它的地址(比如&Foo::x),或者把它绑定到引用,这时候就必须在类外部补充一个定义(不需要再重复赋值,类内的初始化值已经生效):
struct Foo { static int const x = 3; }; // 类外补充定义,无需再次赋值 const int Foo::x;
至于Bjarne的书为什么没特意前置说明“排除类内成员场景”,其实他的表述是先讲通用规则,这类特殊例外通常会在后续讲解类成员的章节里专门覆盖。而且从语言设计的角度,类内静态成员的声明/定义分离是为了支持编译单元的分离编译,这个特例也是为了兼顾常量表达式的便捷性和分离编译的规则。
所以你完全不用怀疑Bjarne的表述,只是这个场景属于通用规则之外的特殊情况,标准单独做了规定而已。
内容来源于stack exchange

