声明普通类和类模板的静态数据成员
Declaring static data members of normal class and class template
我读到在源文件中定义静态数据成员的原因是因为如果它们在头文件中并且多个源文件包含头文件-定义将多次输出。我可以理解为什么这对静态const数据成员来说是个问题,但为什么这对静态数据成员来说是个问题呢?
我不太确定我完全理解为什么有一个问题,如果定义是写在头文件…
变量的多重定义问题是由于语言定义的两个主要缺陷造成的。
如下所示,您可以轻松地绕过。没有技术原因可以解释为什么没有直接支持。这与该功能没有足够高的需求有关,因此委员会的人选择将其作为优先级。
首先,为什么多个定义通常是一个问题。由于c++缺乏对单独编译模块的支持(缺陷#1),程序员必须通过使用文本预处理等来模拟该特性。然后很容易不经意地引入两个或多个相同名称的定义,这很可能是错误的。
对于函数,这可以通过inline
关键字和属性来解决。独立函数只能显式地为inline
,而成员函数可以通过在类定义中定义而隐式地为inline
。无论哪种方式,如果一个函数是inline
,那么它可以在多个翻译单元中定义,并且它必须在使用它的每个翻译单元中定义,并且这些定义必须是等价的。
这个解决方案主要允许在头文件中定义类。
不需要这样的语言特性来支持头文件中定义的数据和变量,所以它不存在:你不能有inline
变量。这是语言缺陷#2。
static
数据成员的特殊豁免来获得inline
变量的效果。豁免的原因是类模板通常必须在头文件中完全定义(除非模板仅在翻译单元中内部使用),因此对于能够具有static
数据成员的类模板,有必要从一般规则中豁免,或者提供一些特殊支持。委员会选择了规则豁免路线。
template< class Dummy >
struct math_
{
static double const pi;
};
template< class Dummy >
double const math_<Dummy>::pi = 3.14;
typedef math_<void> math;
以上被称为模板化的const技巧。据我所知,是我在[comp.lang.c++] Usenet组中介绍了它,所以我不能把功劳归功于别人。我也在SO上发过几次。
无论如何,这意味着每个c++编译器和链接器内部都支持并且必须支持inline
数据所需的机制,而语言却没有这个特性。
constexpr
,你可以把上面的代码写成
struct math
{
static double constexpr pi = 3.14;
};
好吧,有一个区别,你不能取c++ 11 math::pi
的地址,但这是一个非常小的限制。
我认为你混淆了两件事:static
数据成员和static
全局变量市场。
#include
,每个翻译单元将收到这些变量的私有副本。标记为const
的全局变量默认具有内部链接,因此您不需要为它们显式指定static
。因此,链接器不会抱怨全局const
变量或标记为static
的全局非const
变量的多个定义,而在其他情况下它会抱怨(因为这些变量将具有外部链接)。
关于static
数据成员,这是c++ 11标准第9.4.2/5段所说的:
static
数据成员在命名空间作用域中具有外部链接(3.5)。一个地方性的班级不得有static
数据成员。
这意味着如果你把它们的定义通过多个翻译单元放在头文件 #include
d中,你将在相应的目标文件中得到相同符号的多个定义(就像非const
全局变量一样),不管它们的const
限定是什么。在这种情况下,您的程序将违反单定义规则。
此外,这个关于StackOverflow的问答可以让您更清楚地了解主题。
- 静态数据成员的问题-修复链接错误会导致编译器错误
- 静态数据成员模板专用化的实例化点在哪里
- 调用在 HXX 文件中声明的静态数据成员
- 虚拟成员函数的定义是否强制在同一转换单元中动态初始化静态数据成员?
- 错误: 无效使用非静态数据成员"应用程序::应用程序构造函数"
- 静态数据成员:它"const declaration / constexpr definition"起作用?
- 何时需要定义类的静态数据成员 (un/-)
- 为什么静态数据成员不能在c++11中的类中初始化
- 静态数据成员的模板专用化
- 拒绝包含某些公共静态数据成员的类型
- GCC:在调试构建中优化的静态数据成员
- 类模板静态数据成员定义/声明/初始化
- C++ 中的静态数据成员
- 如果类在 C++ 中具有常量或引用类型的非静态数据成员,为什么编译器不提供默认赋值运算符?
- 使用 lambda 函数初始化静态数据成员
- 引用静态数据成员
- 学习C++并在早期示例中遇到错误(在非静态数据成员之前需要构造函数)
- Windows 上的 Clang/LLVM 7 和 8 多次初始化内联静态数据成员(使用 link.exe 和 lld-
- 为什么不能使用"( )"为类的非静态数据成员提供默认值?
- 不带初始值设定项的 constexpr 静态数据成员