Defining static const integer members in class definition

My understanding is that C++ allows static const members to be defined inside a class so long as it's an integer type.

Why, then, does the following code give me a linker error?

#include <algorithm>
#include <iostream>

class test
{
public:
    static const int N = 10;
};

int main()
{
    std::cout << test::N << "\n";
    std::min(9, test::N);
}

The error I get is:

test.cpp:(.text+0x130): undefined reference to `test::N'
collect2: ld returned 1 exit status

Interestingly, if I comment out the call to std::min, the code compiles and links just fine (even though test::N is also referenced on the previous line).

Any idea as to what's going on?

My compiler is gcc 4.4 on Linux.


Solution 1:

My understanding is that C++ allows static const members to be defined inside a class so long as it's an integer type.

You are sort of correct. You are allowed to initialize static const integrals in the class declaration but that is not a definition.

Interestingly, if I comment out the call to std::min, the code compiles and links just fine (even though test::N is also referenced on the previous line).

Any idea as to what's going on?

std::min takes its parameters by const reference. If it took them by value you'd not have this problem but since you need a reference you also need a definition.

Here's chapter/verse:

9.4.2/4 - If a static data member is of const integral or const enumeration type, its declaration in the class definition can specify a constant-initializer which shall be an integral constant expression (5.19). In that case, the member can appear in integral constant expressions. 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.

See Chu's answer for a possible workaround.

Solution 2:

Bjarne Stroustrup's example in his C++ FAQ suggests you are correct, and only need a definition if you take the address.

class AE {
    // ...
public:
    static const int c6 = 7;
    static const int c7 = 31;
};

const int AE::c7;   // definition

int f()
{
    const int* p1 = &AE::c6;    // error: c6 not an lvalue
    const int* p2 = &AE::c7;    // ok
    // ...
}

He says "You can take the address of a static member if (and only if) it has an out-of-class definition". Which suggests it would work otherwise. Maybe your min function invokes addresses somehow behind the scenes.

Solution 3:

Another way to do this, for integer types anyway, is to define constants as enums in the class:

class test
{
public:
    enum { N = 10 };
};