声明普通类和类模板的静态数据成员

时间:2013-03-15 23:51:14

标签: c++ oop static const

我读到在源文件中定义静态数据成员的原因是因为如果它们在头文件中并且多个源文件包含头文件 - 定义将多次输出。我可以看到为什么这对于静态const数据成员来说会有问题,但为什么这对静态数据成员来说是一个问题呢?

我不太确定如果定义是在头文件中写的,我完全理解为什么会出现问题......

2 个答案:

答案 0 :(得分:2)

变量的多重定义问题是由于语言定义中的两个主要缺陷。

如下图所示,您可以轻松解决它。没有技术原因没有直接支持。这与委员会中人们选择将其作为优先事项没有足够高要求的特征有关。

首先,为什么多个定义通常是个问题。由于C ++缺乏对单独编译模块的支持(缺陷#1),程序员必须通过使用文本预处理等来模拟该功能。然后很容易无意中引入两个或多个相同名称的定义,很可能是错误的。

对于函数,这由inline关键字和属性解决。独立函数只能显式inline,而成员函数可以通过在类定义中定义来隐式inline。无论哪种方式,如果函数是inline,那么它可以在多个翻译单元中定义,并且必须在每个使用它的翻译单元中定义,并且这些定义必须是等价的。

主要是该解决方案允许在头文件中定义类。

不需要这样的语言功能来支持数据,头文件中定义的变量,所以它不存在:你不能拥有inline个变量。这是语言缺陷#2。

但是,您可以通过对类模板的inline数据成员的特殊豁免来获取static变量的效果。豁免的原因是类模板通常必须在头文件中完全定义(除非模板仅在翻译单元内部使用),因此类模板能够拥有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数据所需的机制,但该语言没有该功能。

然而,第三方面,C ++ 11有constexpr,你可以在上面写上

struct math
{
    static double constexpr pi = 3.14;
};

嗯,有一点不同,你不能得到C ++ 11 math::pi的地址,但这是一个非常小的限制。

答案 1 :(得分:1)

我认为你混淆了两件事:static数据成员和全局变量标记为static

后者具有内部链接,这意味着如果您将其定义放在多个翻译单元#include的头文件中,则每个翻译单元将收到私有副本< / em>这些变量。

标记为const的全局变量默认具有内部链接,因此您无需为这些明确指定static。因此,链接器不会抱怨全局const变量或标记为const的全局非static变量的多个定义,而在其他情况下会抱怨(因为这些变量)会有外部联系)。

关于static数据成员,这就是C ++ 11标准的第9.4.2 / 5段所说的:

  命名空间范围中的类的

static数据成员具有外部链接(3.5)。本地班级不得   static个数据成员。

这意味着如果您通过多个翻译单元将其定义放在头文件#include d中,您将在相应的对象文件中得到相同符号的多个定义(与非{{1完全相同)全局变量),无论他们的const资格是什么。在这种情况下,您的计划会违反 One Definition Rule

此外,this Q&A on StackOverflow可能会让您更清楚地了解这个主题。