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

Why can't we have non-const class-level static variables?

本文关键字:静态 变量 常量 我们 不能 非常 为什么      更新时间:2023-10-16

为什么Visual c++编译器拒绝编译这段代码?

我显然知道错误是:

错误C2864: Singleton<T>::p:
只能在类中初始化静态const整型数据成员

, ?(即,是否存在技术不允许的原因?)
这是编译器特有的行为还是标准强制要求的?
在全局作用域似乎没问题,那么为什么在类作用域不行呢?

似乎并不是所有的编译器都介意这个

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

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

这是标准行为。只有静态const整型成员可以在没有适当定义的情况下初始化。所有其他类型都需要在某个地方定义,并且在定义点编写初始化:

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

对象必须在某处定义。如果在类中定义它们,则在头文件中定义它们,并且为包含它们的每个编译单元获得不同的对象。这对于const整型来说是宽松的,如果你没有定义它们,编译器就会用它的字面值替换它。如果没有提供定义,获取此类静态const整型的地址仍然会导致链接器错误。

可以使用这种类型的变量,但不能在类定义中初始化它。唯一可以按您要求的方式初始化的变量类型是static const

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

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

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

正如每个人指出的那样,你不能在类的主体中定义非const,非整型(至少在c++ 03中不能,它在c++ 11中改变了,但我不确定具体是如何改变的)。然而,你可以用不同的方法来清理。

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

声明应该放在头文件中,在那里它们将被编译许多次-每个包含它们的地方。

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

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

不能像这样在类体中分配非静态类。相反,应该在类之外赋值(通常在您的cpp文件中)

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

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

为什么允许在声明中为const整型静态成员赋值?

问题的重要部分是声明。变量获得的值是在变量定义中设置的,这与类的非const或非整型静态成员一致,在类中只提供声明。初始化器值在定义中提供,该定义位于类定义之外,通常在.cpp中,保证它将定义在单个翻译单元中。

但是为什么整型静态常量可以在声明中有值呢?

出于实际原因。编译器可以使用整型常量作为编译时常量,也就是说,它实际上可以在所有将该常量用作右值的地方(例如定义数组的大小时)替换该常量的来代替标识符。但是,如果仅在单个翻译单元的定义中出现,则编译器不可能在所有其他翻译单元中使用它。例如:

// 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的大小,这是一个非常明智的使用静态常量。

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

因为它似乎没有多大意义…在类定义中提供的值不能被编译器在任何其他情况下使用,因此它是不需要的,所以只有整型常量需要被区别对待。

原因是非整型、非const值需要一个内存位置。

const int可以由编译器静态处理,并直接构建到某些机器指令,浮点数和更多的外来对象需要一个地方驻留,因为机器指令只根据它们的地址进行操作。

原则上,语言可以允许这样做,但这意味着要么生成额外的对象并使二进制文件膨胀(对于const来说是ok的),要么使编译器编写非const的工作变得困难:必须删除冗余副本以保持单定义规则(顺便说一下,这就是模板实例化必须做的)。