为什么我们不能拥有非const类级静态变量?

时间:2011-10-17 16:50:29

标签: c++ visual-c++ singleton static-members

为什么the Visual C++ compiler refuse to compile this code

我显然知道错误是:

  

错误C2864:Singleton<T>::p
  只能在类

中初始化静态const积分数据成员

为什么? (即是否有技术理由不允许这样做?)
这是特定于编译器的行为还是由标准规定的?
它似乎在全球范围内没有问题,为什么不在课堂范围内?

它似乎也是not all compilers mind this

另外,解决这个问题的正确方法是什么?

template<typename T>
struct Singleton
{
    static T *p = 0;  // Error C2864

    static T *getInstance() { /*...*/ return p; }
};

7 个答案:

答案 0 :(得分:9)

这是标准行为。只有静态const积分成员才能在没有正确定义的情况下进行初始化。需要在某处定义所有其他类型,并在定义点写入初始化:

template<typename T>
struct Singleton {
    static T *p;

    static T *getInstance() { /*...*/ return p; }
};

template<typename T>
T *Singleton<T>::p = 0;

必须在某处定义对象。如果在类中定义它们,则在头文件中定义它们,并为包含它的每个编译单元获取不同的对象。这对于const整数类型是放宽的,如果你没有定义它们,那么编译器只是用它的文字值替换它。如果没有提供定义,那么取这样的静态const积分的地址仍会导致链接器错误。

答案 1 :(得分:4)

您可以拥有此类型的变量,但不能在类定义中初始化它。可以按照您要求的方式初始化的唯一变量类型是static const

您的类定义的固定版本会删除= 0,并将其添加到类定义下方:

template<typename T>
T *Singleton<T>::p = 0;

这是标准行为。我不确定是否存在技术原因,我的猜测是与实例成员保持一致(也不能以这种方式初始化)。当然,实例变量具有构造函数初始化列表。

答案 2 :(得分:4)

正如大家所指出的那样,你不能在类的主体中定义一个非const,非整数类型(至少不能用C ++ 03,它改变了C ++ 11,但我不知道如何究竟)。但是,您可以采用不同的方式进行清洁。

template<typename T>
struct Singleton {
    static T* getInstance() {
        static T* p = NULL;
        /*...*/
        return p;
    }
};

答案 3 :(得分:3)

声明是为了进入头文件,它们将被编译多次 - 每个地方都包含它们。

静态变量应该只有一个定义,因此整个程序中只存在一个副本。这意味着它需要位于源(.cpp)文件中。分配值需要放在那个位置。

静态常量整数是上述规则的一个例外,因为它们可以成为编译时常量。当它们被使用时,用文字值代替类成员。

答案 4 :(得分:1)

你不能像这样在类体中指定非静态。相反,在类外部(通常在cpp文件中)分配值

template<typename T>
struct Singleton {
    static T *p;

    static T *getInstance() { /*...*/ return p; }
};

template<typename T>
T *Singleton<T>::p = 0;

答案 5 :(得分:1)

有趣的问题不是你提出的问题,而是相反的问题:

为什么允许在声明中为const积分静态成员分配值?

问题的重点是声明。变量获取的值在变量 definition 中设置,并且与类的非const或非整数静态成员一致,在类中只提供声明。初始化器值在类定义之外的定义中提供,通常在.cpp中,以保证它将在单个转换单元中定义。

但为什么整数静态常量在声明中有一个值?

出于实际原因。编译器可以使用积分常量作为编译时常量,也就是说,它实际上可以替换常量的来代替常量所在的所有位置的标识符用作 rvalue ,例如在定义数组的大小时。但是,如果仅存在于单个转换单元的定义中,则编译器不可能在所有其他转换单元中使用它。举个例子:

// fq.h
struct fixed_queue {
   static const std::size_t max_elements; // [1]
   int data[ max_elements ];              // Error: How big is data??
};
// fq.cpp
#include "fq.h"
const std::size_t fixed_queue::max_elements = 10;

如果max_elements不允许在声明[1]中有值,那么你将无法使用该常量来定义数组data的大小,这是一个相当的明智地使用静态常数。

为什么不将此扩展到所有其他情况?

因为它似乎没有多大意义......在任何其他情况下,编译器不能使用类定义中的值,因此不需要它,因此只需要对不同的整数常量进行处理。

答案 6 :(得分:0)

原因是非整数非const值需要内存位置。

const int可以由编译器静态处理并直接构建到某些机器指令中,浮点数和更奇特的对象需要在某处生存,因为机器指令只能根据其地址运行。

原则上,语言可以允许这样做,但这意味着要么生成额外的对象,要么膨胀二进制文件(对于consts来说是好的),要么让编译器编写者为非consts做艰苦的生活:冗余的副本必须是删除以保留单一定义规则(顺便说一下,这是模板瞬时必须执行的操作)。