开发者

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

Why does the Visual C++ compiler refuse to compile this code?

I obviously know that the error is:

Error C2864: Singleton<T>::p:

Only static const integral data members can be initialize开发者_StackOverflow中文版d within a class

but why? (i.e. is there a technical reason why it is not allowed?)

Is this compiler-specific behavior or is it mandated by the standard?

It seems to be fine at the global scope, so why not at the class scope?

It also seems like not all compilers mind this.

Also, what is the proper way of fixing this?

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

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


That's standard behavior. Only static const integral members can be initialized without a proper definition. All other types need to be defined somewhere, and the initialization is written at the point of definition:

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

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

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

Objects must be defined somewhere. If you define them within your class then its defined at a header file, and you get a different object for each compilation unit that includes it. This is relaxed for const integral types and if you don't define them then the compiler just replaces it with its literal value. Taking the address of such static const integral would still result in a linker error if no definition is provided.


You can have this type of variable, but you cannot initialize it inside the class definition. The only type of variable that may be initialized the way you are asking is a static const.

A fixed version of your class definition removes the = 0, and adds this below the class definition:

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

This is standard behavior. I'm not sure there is a technical reason, my guess would be it is for consistency with instance members (which cannot be initialized this way, either). Instance variables, of course, have constructor initializer lists instead.


As everyone pointed out, you cannot define a non-const, non-integral type in the body of a class (at least not with C++03, it changed with C++11 but I'm not sure how exactly). However, you can do it differently and clean.

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


Declarations are meant to go into header files, where they will be compiled many times - each place they are included.

Static variables should only have one definition, so that only one copy will exist in the entire program. This means it needs to be in a source (.cpp) file. Assigning the value needs to be put at that spot.

Static constant integers are an exception to the above rules, because they can become compile time constants. When they're used, a literal value is substituted for the class member.


You cannot assign a non-static in the class body like that. Instead, assign the value outside the class (usually in your cpp file)

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

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

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


The interesting question is not the one you ask, but rather the opposite:

Why are const integral static members allowed to be assigned a value in the declaration?

The important bit of the question is declaration. The value that a variable gets is set in the variable definition, and that is consistent with non-const or non-integral static members of a class, where in the class you only offer a declaration. The initializer value is provided in the definition that is outside of the class definition, commonly in a .cpp that guarantees that it will be defined in a single translation unit.

But why can integral static constants have a value in the declaration?

For practical reasons. The compiler can use integral constants as compile-time constants, that is, it can actually replace the value of the constant in place of the identifier in all places where the constant is used as an rvalue, for example when defining the size of an array. But if the value is only present in the definition in a single translation unit, the compiler cannot possibly use it in all other translation units. As an example:

// 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;

If max_elements was not allowed to have a value in the declaration [1], then you would not be able to use that constant to define the size of the array data, which is a quite sensible use for a static constant.

Why not extend this to all other cases?

Because it does not seem to make much sense... Providing the value in the class definition cannot be used by the compiler in any other circumstance, and thus it is not needed, so only integral constants need to be treated differently.


The reason is that non-integral, non-const values require a memory location.

const int can be handled statically by the compiler and built directly into certain machine instructions, floats and more exotic objects need somewhere to live because the machine instructions will only operate in terms of their address.

In principle, the language could have allowed this, but it would mean either generating extra objects and bloating the binary (ok for consts), or making life hard for the compiler writers for non-consts: redundant copies would have to be dropped to preserve the one-definition rule (this is what template instantinations have to do, by the way).

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜