Undefined reference to static class member

C++G++

C++ Problem Overview


Can anyone explain why following code won't compile? At least on g++ 4.2.4.

And more interesting, why it will compile when I cast MEMBER to int?

#include <vector>

class Foo {  
public:  
    static const int MEMBER = 1;  
};

int main(){  
    vector<int> v;  
    v.push_back( Foo::MEMBER );       // undefined reference to `Foo::MEMBER'
    v.push_back( (int) Foo::MEMBER ); // OK  
    return 0;
}

C++ Solutions


Solution 1 - C++

You need to actually define the static member somewhere (after the class definition). Try this:

class Foo { /* ... */ };

const int Foo::MEMBER;

int main() { /* ... */ }

That should get rid of the undefined reference.

Solution 2 - C++

The problem comes because of an interesting clash of new C++ features and what you're trying to do. First, let's take a look at the push_back signature:

void push_back(const T&)

It's expecting a reference to an object of type T. Under the old system of initialization, such a member exists. For example, the following code compiles just fine:

#include <vector>

class Foo {
public:
    static const int MEMBER;
};

const int Foo::MEMBER = 1; 

int main(){
    std::vector<int> v;
    v.push_back( Foo::MEMBER );       // undefined reference to `Foo::MEMBER'
    v.push_back( (int) Foo::MEMBER ); // OK  
    return 0;
}

This is because there is an actual object somewhere that has that value stored in it. If, however, you switch to the new method of specifying static const members, like you have above, Foo::MEMBER is no longer an object. It is a constant, somewhat akin to:

#define MEMBER 1

But without the headaches of a preprocessor macro (and with type safety). That means that the vector, which is expecting a reference, can't get one.

Solution 3 - C++

The C++ standard requires a definition for your static const member if the definition is somehow needed.

The definition is required, for example if it's address is used. push_back takes its parameter by const reference, and so strictly the compiler needs the address of your member and you need to define it in the namespace.

When you explicitly cast the constant, you're creating a temporary and it's this temporary which is bound to the reference (under special rules in the standard).

This is a really interesting case, and I actually think it's worth raising an issue so that the std be changed to have the same behaviour for your constant member!

Although, in a weird kind of way this could be seen as a legitimate use of the unary '+' operator. Basically the result of the unary + is an rvalue and so the rules for binding of rvalues to const references apply and we don't use the address of our static const member:

v.push_back( +Foo::MEMBER );

Solution 4 - C++

Aaa.h

class Aaa {

protected:
	
	static Aaa *defaultAaa;

};

Aaa.cpp

// You must define an actual variable in your program for the static members of the classes

static Aaa *Aaa::defaultAaa;

Solution 5 - C++

In C++17, there is an easier solution using inline variables:

struct Foo{
    inline static int member;
};

This is a definition of member, not just its declaration. Similar to inline functions, multiple identical definitions in different translation units do not violate ODR. There is no longer any need to pick a favourite .cpp file for the definition.

Solution 6 - C++

Just some additional info:

C++ allows to "define" const static types of integral and enumeration types as class members. But this is actually not a definition, just an "initializiation-marker"

You should still write a definition of your member outside of the class.

>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.

Solution 7 - C++

No idea why the cast works, but Foo::MEMBER isn't allocated until the first time Foo is loaded, and since you're never loading it, it's never allocated. If you had a reference to a Foo somewhere, it would probably work.

Solution 8 - C++

With C++11, the above would be possible for basic types as

class Foo {
public:  
  static constexpr int MEMBER = 1;  
};

The constexpr part creates a static expression as opposed to a static variable - and that behaves just like an extremely simple inline method definition. The approach proved a bit wobbly with C-string constexprs inside template classes, though.

Solution 9 - C++

Regarding the second question: push_ref takes reference as a parameter, and you cannot have a reference to static const memeber of a class/struct. Once you call static_cast, a temporary variable is created. And a reference to this object can be passed, everything works just fine.

Or at least my colleague who resolved this said so.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionPawel PiatkowskiView Question on Stackoverflow
Solution 1 - C++Drew HallView Answer on Stackoverflow
Solution 2 - C++Douglas MayleView Answer on Stackoverflow
Solution 3 - C++Richard CordenView Answer on Stackoverflow
Solution 4 - C++iso9660View Answer on Stackoverflow
Solution 5 - C++QuimbyView Answer on Stackoverflow
Solution 6 - C++MarcView Answer on Stackoverflow
Solution 7 - C++Paul TomblinView Answer on Stackoverflow
Solution 8 - C++starturtleView Answer on Stackoverflow
Solution 9 - C++QuarraView Answer on Stackoverflow