简体   繁体   中英

C++ using a static const class member in a template

So I have this c++ code which I have written for c++0X. It used to compile in MSVC 2012, but now I switched to MingW64 4.8.1 because I was dissatisfied with the lack of C++11 support in MSVC. The following is part of some code which implements a simple Entity/Component System.

This is the error I get:

if(e->components.find(T::ID) == e->components.end())

undefined reference to `EL::Position3DComponent::ID' ELEntityManager.h /Elementium/Elementium line 64 C/C++ Problem

which is related to using T::ID...

Here is some further explanation of what I used to use this code for in MSVC 2012:

In every component, I have a static const ELComponentID member which is initialized to the component's id. This is used because I need to easily get the entities which have a certain component, so I'm using a multimap in the ELEntityManager whose key is ELComponentID and whose value is a unique_ptr containing the ELEntity who has such a component.

In the ELEntity class, I use an unordered_map whose key is ELComponentID and whose value is a unique_ptr containing the ELComponent in question.

Yes it does take up a little more memory, but I do this mainly for access speed.

file ELEntityManager.h:

//Includes
#include <map>
#include <memory>
#include "ELEntity.h"
#include "ELComponent.h"

namespace EL{

class ELEntityManager
{
public:

//...

template<typename T> void addComponent(std::unique_ptr<ELEntity> e, std::unique_ptr<ELComponent> c)
{
    if(c == nullptr || e == nullptr)
        return;
    if(e->components.find(T::ID) == e->components.end())  //***** <-- This is where I get the error.
    {
        //...
    }
    //...
}

//...

private:
//******************************************
// Private data members
//******************************************
    std::multimap<ELComponentID, std::unique_ptr<ELEntity> > entities;
};    

};// End namespace

file ELEntity.h:

//Includes
#include <unordered_map>
#include <memory>
#include "ELComponent.h"

namespace EL{

class ELEntity
{
    friend class ELEntityManager;

//...

private:
//******************************************
// Private data members
//******************************************
    /**The map of ComponentIDs with their components.*/
    std::unordered_map<ELComponentID, std::unique_ptr<ELComponent> > components;
};

};// End namespace

file ELComponent.h:

//Includes
#include <unordered_map>
#include <functional>
#include <string>
#include <vector>
#include "ELMath.h"


namespace EL{

/**
* Component IDs.
*/
enum ELComponentID {
    LogicalDevice = 1,  // Start the enum at 1.
    Viewport,
    Position3D,
    Position2D,
    Orientation,
    PhysicsRK4
};

/**
* Base component class.
*/
struct ELComponent
{
};

/**
* Position3D component, derives from ELVector3D in EL::Math.
*/
struct Position3DComponent: public ELComponent, EL::Math::ELVector3D
{
    static const ELComponentID ID = Position3D;
};

//...

then I have this in main.cpp as a test (with all the required includes, etc...):

EL::ELEntityManager em;

std::unique_ptr<EL::ELEntity> e(new EL::ELEntity());
std::unique_ptr<EL::Position3DComponent> obj(new EL::Position3DComponent());

obj->x = 1.0;
obj->y = 2.0;
obj->z = 3.0;

em.addComponent<EL::Position3DComponent>(std::move(e), std::move(obj));

Now my question is, am I doing something wrong which is gcc specific, is the T::ID not supported in gcc/mingw, or has anything changed in the final c++11 implementation which wasn't in for MSVC 2012?

How do I fix this error? If it can't be done anymore in c++11, or if there's a bug in gcc, can I do this any other way?

Thanks a lot in advance for your replies! :)

I think GCC is right. From the code you posted, it seems to me you are not providing a definition for your static data member.

Since you are passing T::ID in input to std::unordered_map::find() , which takes its argument by reference , you are odr-using ID (ODR stands for One Definition Rule, and odr-using means, in short, that the compiler needs to know the address of that object).

Since the address of the static data member is needed, but no definition at global namespace is provided, you end up with an unresolved symbol error from the linker.

Per paragraph 9.4.2/3 of the C++11 Standard:

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). [...] 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 .

Therefore, to solve the problem, just add a definition at namespace scope in a .cpp file (or in a header included by just one .cpp file):

const ELComponentID Position3DComponent::ID;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM