Q:

Difference between Linux gcc and Windows Visual Studio handling of static constants?

We have been compiling a library on both Linux (gcc) and Windows (Visual Studio), and as expected, finding minor, but not significant differences between what it takes to get a clean compile on both platforms.
Today, I changed gcc compiler flag to use -fPIC (to enable making a shared library).  When we tested linking a program against the library, we started getting errors (for the first time), with  undefined reference to 2 static constants which are declared and initialized in the header file (but not in .cpp file).  
I found this StackOverflow answer which seemed to address the problem, explaining that, even if static const is initialized in header file, it still needs to be defined in the code file. And making that change did remove the gcc linker error.
Visual Studio, however, didn't like that change, and generated multiple definition errors.  We had to wrap the definition needed a preprocessor conditional to get Visual Studio to compile cleanly.
Can someone enlighten me as to what the difference is here?  (Code excerpt is below.)
msg.h
class msg
{
  public:
    static const int EMPTY_INT_VALUE = INT_MAX;
    static const char EMPTY_STRING_VALUE = '\002';
    // can't define value in header, defined in cpp file
    static const double EMPTY_DOUBLE_VALUE;   
    ...
}

msg.cpp
#include "msg.h"

const double msg::EMPTY_DOUBLE_VALUE(DBL_MAX);

#ifndef _WIN32
// g++ requires these definitions, vs 2010 doesn't like them
const int msg::EMPTY_INT_VALUE;
const char msg::EMPTY_STRING_VALUE;
#endif

A:

I've tracked it down to section 9.4.2 "Static data members" of the C++ language specification (INCITS/ISO/IEC 14882-2011[2012]):

If a non-volatile const static data member is of integral or enumeration type, its declaration in the class definition can specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression (5.19). A static data member of literal type can be declared in the class definition with the constexpr specifier; if so, its declaration shall specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression. [ Note: In both these cases, the member may appear in constant expressions. —end note ] The member shall still be defined in a namespace scope if it is odr-used (3.2) in the program and the namespace scope definition shall not contain an initializer.

So, for integral types (e.g. int and char in your example), you can initialize in the class definition. If you do so, you must also define it at namespace scope (i.e. in your .cpp file) and without an initializer. Elsewhere in that section of the spec it is stated that declaration of a static data member in its class is not a definition and must, therefore, be accompanied by a definition at namespace scope, which is what you're doing in your example.
GCC follows this part of the specification, Visual C++ doesn't. I tested with Visual Studio 2012.
Here's another discussion of Visual C++'s noncompliance in this regard. The workaround described here is exactly what you're doing: guard the definition of the integral variables, although they're using _MSC_VER.

