Partilhar via


VC's "evil" extension: Implicit definition of static constant member

C++ supports in-class initialization of static integral constant members. It is nearly the same as enum, with the following difference (quoted from C++03 9.4.2.4 ([class.static.data])):

The member shall still be defined in a namespace scope if it is used in the program and the namespace scope definition shall not contain an initializer.

For example:

struct S {
    static const int i = 0;
};

const int S::i; // the definition is required if S::i is used

int main()
{
    return &S::i ? 1 : 0;
}

However, if you compile the above code in VC, you'll find that it links successfully even without the definition!

The magic is told in <xstddef> (under VSInstallFolder\VC\include)

 #define _STCONS(ty, name, val)    static const ty name = (ty)(val)
 #if !defined(_MSC_EXTENSIONS) && !(defined(_DLL) && !defined(_STATIC_CPPLIB))
  // Under /Ze, static const members are automatically defined, so provide a
  // definition only under /Za, and only when __declspec(dllimport) not used.
  #define _STCONSDEF(cls, ty, name) __declspec(selectany) const ty cls::name;
 #else
  #define _STCONSDEF(cls, ty, name)
 #endif

This "evil" extension eases the usage of static constant member. Without it, you have to ensure that you define the constant exactly once somewhere. It is hard for header-only libary.

One side effect of this evil extension is, you should no longer define static constant member by yourselves. Otherwise it will conflict with the implitly defined one by the compiler and you'll get a linker error because of the violation of one definition rule (ODR). Like:

main.obj : error LNK2005: "public: static int const S::i" (?i@S@@2HB) already defined in other.obj 

BTW, template is different. See the following code:

struct S {
    static const int i;
};
const int S::i = 0; // redefinition error if the code is in a header and the header is included by multiple source files

template<typename T>
struct ST {
    static const T i;
};
template<typename T>
const T ST<T>::i(0); // OK

The standard says: "There can be more than one definition of ..., or static data member of a class template, ... provided that each definition appears in a different translation unit, and provided the definitions satisfy the following requirements ..."