Definition needed
The code you have provided is non-standard. While you can provide initializers for const static int members directly in the class, you still need to provide separate definitions. It is weird, a kind of unexpected, but you are expected to write it like this:
#include <algorithm>
struct Foo
{
static const int A = 1;
static const int B = 2;
};
const int Foo::A;
const int Foo::B;
int main()
{
return std::min(Foo::A, Foo::B);
}
Quote from standard can be found in a similar question at const and static specifiers in c++
Why sometimes the code "works" without a definition?
As for why you can often get around even without providing the definition: if you are using those members only in constant expressions, compiler will always resolve them directly and there will be no access left for linker resolution. It is only when you use it in some way which cannot be handled by compiler directly, and only in such case the linker will detect the symbol is undefined. I guess this is probably a bug in the Visual Studio compiler, but given the nature of the bug I doubt it will be ever fixed.
Why your source falls into the "linker" category is something I do not see, one would need to dissect the std::min to understand that. Note: When I have tried it online with GCC, it worked, the error was not detected.
Alternative: use enum
Another alternative is to use enum. This version can also come handy when you hit an old compiler which does not support static const int "inline" initializers (such as was Visual Studio 6). Note however that with std::min you are hitting other problems with enums and you need to use an explicit instantiation or casting, or have both A and B in one named enum as in the answer by Nawaz:
struct Foo
{
enum {A = 1};
enum {B = 2};
};
int main()
{
return std::min<int>(Foo::A, Foo::B);
}
Standards
Note: even Stroustrup C++ FAQ gets this wrong and does not require the definition as strictly as the standard does:
You can take the address of a static member if (and only if) it has an out-of-class definition
The definition is required by a standard in 9.4.2:
C++03 wording:
The member shall still be defined in a name-space scope if it is used in the program and the namespace scope definition shall not contain an initializer
C++11 wording of 9.4.2 is a bit different:
3 The member shall still be de?ned in a namespace scope if it is odr-used (3.2) in the program
3.2 says following about odr-use:
3 A variable x whose name appears as a potentially-evaluated expression ex is odr-used unless x is an object that satis?es the requirements for appearing in a constant expression (5.19) and ex is an element of the set of potential results of an expression e, where either the lvalue-to-rvalue conversion (4.1) is applied to e, or e is a discarded-value expression (Clause 5).
4 Every program shall contain exactly one de?nition of every non-inline function or variable that is odr-used in that program; no diagnostic required.
I have to admit I am not sure what the exact implications of C++11 wording are, as I fail to understand the odr-use rules.