C ++在模板中使用静态const类成员

时间:2013-06-12 20:07:42

标签: c++ templates c++11 const static-members

所以我有这个我为c ++ 0X编写的c ++代码。它曾用于在MSVC 2012中编译,但现在我切换到MingW64 4.8.1,因为我对MSVC中缺少C ++ 11支持感到不满意。以下是一些实现简单实体/组件系统的代码的一部分。

这是我得到的错误:

  

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

     

未定义的引用   `EL :: Position3DComponent :: ID' ELEntityManager.h / Elementium / Elementium行   64 C / C ++问题

与使用T :: ID ...

有关

以下是我在MSVC 2012中使用此代码的一些进一步说明:

在每个组件中,我都有一个静态const ELComponentID成员,它被初始化为组件的id。使用它是因为我需要轻松获取具有特定组件的实体,因此我在ELEntityManager中使用多重映射,其键是ELComponentID,其值是包含具有此类组件的ELEntity的unique_ptr。

在ELEntity类中,我使用了一个unordered_map,其键是ELComponentID,其值是包含所讨论的ELComponent的unique_ptr。

是的,它确实占用了更多的内存,但我主要是为了访问速度。

文件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

文件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

文件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;
};

//...

然后我在main.cpp中将它作为测试(包含所有必需的包括等等):

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));

现在我的问题是,我做错了什么是gcc特有的,gcc / mingw中不支持T :: ID,或者在最终的c ++ 11实现中没有任何改变对于MSVC 2012?

如何修复此错误?如果它不能在c ++ 11中完成,或者如果gcc中存在错误,我可以用其他任何方式执行此操作吗?

非常感谢您的回复! :)

1 个答案:

答案 0 :(得分:0)

我认为海湾合作委员会是对的。从您发布的代码中,我觉得您没有为静态数据成员提供定义

由于您将T::ID输入传递给std::unordered_map::find(),其参数通过引用,因此您使用ID(ODR代表One)简而言之,规则和odr使用意味着编译器需要知道该对象的地址。)

由于需要静态数据成员的地址,但未提供全局命名空间的定义,因此最终会出现链接器中未解决的符号错误。

根据C ++ 11标准的第9.4.2 / 3段:

  

如果非易失性const静态数据成员是整数或枚举类型,则其在类中声明   定义可以指定大括号或等于初始值,其中每个 initializer-clause 赋值表达式   是一个常量表达式(5.19)。 [...]仍然应该定义成员   如果在程序中使用odr-used(3.2)并且命名空间作用域定义不在,则在命名空间作用域中   包含初始化程序

因此,要解决此问题,只需在.cpp文件(或只有一个.cpp文件包含的标题)中的命名空间范围添加定义:

const ELComponentID Position3DComponent::ID;