【问题标题】:C++ using a static const class member in a templateC++ 在模板中使用静态 const 类成员
【发布时间】:2013-06-12 20:07:42
【问题描述】:

所以我有这个为 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有bug,我可以用其他方法吗?

非常感谢您的回复! :)

【问题讨论】:

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


    【解决方案1】:

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

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

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

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

    如果一个非易失性的 const 静态数据成员是整数或枚举类型,它在类中的声明 定义可以指定一个 brace-or-equal-initializer,其中每个 initializer-clause 是一个 assignment-expression 是一个常数表达式 (5.19)。 [...] 成员仍应被定义 如果在程序中使用了 odr-used (3.2) 并且命名空间范围定义不应在命名空间范围内 包含一个初始化器

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

    const ELComponentID Position3DComponent::ID;
    

    【讨论】:

    • 照你说的添加 const EL::Position3DComponent::ELComponentID ID;在全局命名空间(或 const Position3DComponent::ELComponentID ID;在 EL 命名空间)会导致以下两个错误: 1-'struct EL::Position3DComponent' 中的'ELComponentID' 没有命名类型 2-Type 'EL::Position3DComponent: :ELComponentID' 无法解析
    • @JPDuchesne:对不起,我在写的时候把事情搞混了。我编辑了答案并提供了正确的语法。
    • 我想我会在 ELComponent.h 中的 Position3DComponent 结构之后添加它,但随后我在包含 ELComponent.h 的所有地方都得到了“EL::Position3DComponent::ID”错误的多重定义(main.cpp、ELEntity.cpp、ELEntityManager.cpp)。有什么见解吗?
    • @JPduchesne:是的,您不能将其添加到包含在多个翻译单元(即 cpp 文件)中的头文件中,否则您将违反单一定义规则。您应该将定义放在一个且仅一个 .cpp 文件中(或将其放在一个且仅一个 .cpp 文件包含的标头中)。
    • 啊,你是对的 :) 所以它确实修复了我的编译错误!但是在执行我的 addComponent 方法时,我确实在运行时崩溃了。也许它搞砸了,我会验证并回复你一个接受的答案:)
    猜你喜欢
    • 1970-01-01
    • 2012-12-13
    • 2012-02-02
    • 2011-03-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多