The program explicitely violates the one definition rule as you have stated in your question. But the standard does not require a diagnostic in this case. This is explicit in 3.2 One definition rule [basic.def.odr] §4 (emphasis mine)
Every program shall contain exactly one definition of every non-inline fonction or variable that is odr-used
in that program; no diagnostic required.
and 1.4 Implementation compliance [intro.compliance] §2.3 says:
If a program contains a violation of a rule for which no diagnostic is required, this International
Standard places no requirement on implementations with respect to that program.
That means that gcc is right to choke on that program because one rule has been violated. But it is not a bug when MSVC accepts it as a compiler extension(*) without even a warning because the standard places no requirement here.
We are in the compile time undefined behaviour named ill-formed program, no diagnostic required. A compiler implementation is free to do what it wants:
- reject the program
- automatically fix the error and generate the code as if the definition was present (for current case)
- remove the offending line[s] if it makes sense and makes the program compilable - even if what is generated is not what the programmer expected
- compile it into a program that ends in a run-time error
- [add whatever you think of, but I have never seen a compiler able to hit my cat...]
(*) more exactly, it is a compiler extension if it is documented. Unfortunately, I currently have no MSVC compiler full documentation, so I can not say whether it is documented and is a conformant extension, or is not and is just hmm... one possible execution of the program
Here is a slight variation on OP's code demonstrating that the compiler did provide a definition:
#include <iostream>
class AE {
// ...
public:
static const int c6 = 7;
static const int c7 = 31;
};
const int AE::c7; // definition
int main()
{
const int* p1 = &AE::c6; // error: c6 not an lvalue
const int* p2 = &AE::c7; // ok
// ...
std::cout << *p1 << "(" << p1 << ") - " << *p2 << "(" << p2 << ")" << std::endl;
return 0;
}
The output is (with an old MSVC 2008, debug mode to avoid as much optimization as possible):
7(00DF7800) - 31(00DF7804)
that is expected values and consecutive addresses
With same code, Clang 3.4 complains (as expected) at link time with
undefined reference to `AE::c6'