1

所以我有这个为 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 中有错误,我可以用其他方式吗?

非常感谢您的回复!:)

4

1 回答 1

0

我认为 GCC 是对的。从您发布的代码来看,在我看来您没有为您的静态数据成员提供定义

由于您将T::ID输入传递给,它通过引用std::unordered_map::find()获取其参数,因此您正在使用 odr (ODR 代表一个定义规则,而 odr-using 意味着编译器需要知道该对象的地址)。ID

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

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

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

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

const ELComponentID Position3DComponent::ID;
于 2013-06-12T20:40:18.990 回答