模板方法调用另一个类的模板方法不想编译

时间:2015-03-14 11:03:13

标签: c++11

我遇到了模板问题。 课程Entity包含std::map个类型(const std::type_info*)和Componentsstd::shared_ptr<Component>)。其中一个组件可以是BehaviorSet,它具有类似的组件映射。但这些组件是Behavior,这是一个派生自Component的类。

起初它没有使用Behaviors和BehaviorSet。我正在使用实体来添加,获取和删除其组件的类型(因此强制每个组件的唯一性)。 当我添加BehaviorSet内容时,它变得奇怪了。我真的很想知道为什么不能编译。 看起来编译器正在尝试编译它不应该的类。

Q.hpp


#include <map>      // std::map
#include <memory>   // std::shared_ptr
#include <typeinfo> // std::type_info

class Component {
public:
    Component() {}
    virtual ~Component() {}
};

class Behavior : public Component {
public:
    Behavior() {}
    virtual ~Behavior() {}
};

class BehaviorSet : public Component {
protected:
    std::map<const std::type_info*, std::shared_ptr<Behavior>> behaviors;
public:

    // Get a pointer to a behavior by its type
    template <typename T>
    T* getBehavior()
    {
        if (behaviors.count(&typeid(T)) != 0)                       // If there's a key for type T ..
            return dynamic_cast<T*>(behaviors[&typeid(T)].get());   // Get the pointer of shared_ptr and cast Behavior to T*            
        return nullptr;                                             // Return NULL on Behavior not found
    }

    // Add a behavior by pointer
    template <typename T>
    void addBehavior(T* behavior)
    {
        if (behaviors.count(&typeid(T)) != 0) return;                // If there's a key, we can't add
        behaviors[&typeid(T)] = std::shared_ptr<Behavior>(behavior); // else we can add the behavior to the map
    }

    // Add a behavior by type
    template <typename T, typename ... Args>
    void addBehavior(Args && ... args)
    {
        addBehavior(new T(std::forward<Args>(args) ...));           // Call addBehavior on a newly created behavior
    }
};


class Entity {
protected:
    std::map<const std::type_info*, std::shared_ptr<Component>> components;
public:

    // Get pointer to component by its type
    template <typename T>
    T* getComponent()
    {
        if (std::is_base_of<Behavior, T>::value) {
            // Hoping there is a BehaviorSet
            return getComponent<BehaviorSet>()->getBehavior<T>();
        }
        if (components.count(&typeid(T)) != 0) return nullptr;  // Return NULL on Component not found
        return dynamic_cast<T*>(components[&typeid(T)].get());  // Get the pointer of shared_ptr and cast Component to T*

    }

    // Add a component by pointer
    template <typename T>
    void addComponent(T* component)
    {
        // If there's already a component of this type, don't even try to add
        if (components.count(&typeid(T)) != 0) return;

        // If it's derived from Behavior, add to BehaviorSet component
        if (std::is_base_of<Behavior, T>::value)
        {
            // Hoping there is a BehaviorSet
            getComponent<BehaviorSet>()->addBehavior<T>(component);
        }
        // It's not derived from Behavior, add it to the component map
        else
        {
            components[&typeid(T)] = std::shared_ptr<Component>(component);
        }
    }

    // Add a component by type
    template <typename T, typename ... Args>
    void addComponent(Args && ... args)
    {
        addComponent(new T(std::forward<Args>(args) ...));
    }
};



class CBehavior : public Behavior {
protected:
    unsigned int value;
public:
    CBehavior(unsigned int arg) : value(arg) {}
};

现在主要是我添加BehaviorSetCBehavior

的main.cpp


int main(int argc, char **argv) {
    Entity entity;
    entity.addComponent<BehaviorSet>();
    entity.addComponent<CBehavior>(1233);
    return 0;
}

它没有编译,我的g ++告诉我这个:

In file included from /usr/include/c++/4.9.2/bits/shared_ptr.h:52:0,
                 from /usr/include/c++/4.9.2/memory:82,
                 from src/Q.hpp:2,
                 from src/game/main.cpp:45:
/usr/include/c++/4.9.2/bits/shared_ptr_base.h: In instantiation of ‘std::__shared_ptr<_Tp, _Lp>::__shared_ptr(_Tp1*) [with _Tp1 = BehaviorSet; _Tp = Behavior; __gnu_cxx::_Lock_policy _Lp = (__gnu_cxx::_Lock_policy)2u]’:
/usr/include/c++/4.9.2/bits/shared_ptr.h:113:32:   required from ‘std::shared_ptr<_Tp>::shared_ptr(_Tp1*) [with _Tp1 = BehaviorSet; _Tp = Behavior]’
src/Q.hpp:36:25:   required from ‘void BehaviorSet::addBehavior(T*) [with T = BehaviorSet]’
src/Q.hpp:77:4:   required from ‘void Entity::addComponent(T*) [with T = BehaviorSet]’
src/Q.hpp:90:3:   required from ‘void Entity::addComponent(Args&& ...) [with T = BehaviorSet; Args = {}]’
src/game/main.cpp:50:35:   required from here
/usr/include/c++/4.9.2/bits/shared_ptr_base.h:871:39: erreur: cannot convert ‘BehaviorSet*’ to ‘Behavior*’ in initialization
         : _M_ptr(__p), _M_refcount(__p)

如果重要,我在最新的archlinux上使用g ++ 4.9.2。

非常感谢你的帮助,我希望我的问题很清楚:)

编辑:我认为我已经减少了代码中的所有功能。

2 个答案:

答案 0 :(得分:0)

我认为您的问题是即使代码永远不会被执行,您也会尝试实例化一个方法。您似乎认为编译器不会尝试基于std::is_base_of<Behavior, T>::value的结果来实例化代码,但实际上,代码将始终被实例化,例如,if和{中的代码{1}}部分。

要解决您的问题,请尝试对在编译时分离的两种情况使用不同的实现。例如,这可能有效:

else

答案 1 :(得分:0)

我已成功解决了最后的问题(主要归功于Daniel Frey)。

首先尝试

addComponent()函数中,我首先将getComponent<BehaviorSet>()更改为直接访问components地图(dynamic_cast<BehaviorSet*> components[&typeid(BehaviorSet)].get()))。它起作用了。这就是让我认为问题出现在getComponent()方法中的原因。

第二次尝试

我尝试使用getComponent()的相同技巧作为addComponent()的技巧。如果条件为真,std::enable_if<bool condition>::type等同于void,则无法直接使用它。解决方法是使其他函数(如果需要,protected)检索指向参数中传递的指针的指针中的组件并返回组件“updated”。它们都有void返回类型,因此std::enable_if诀窍在这里有用。

必须使用指向Component指针的指针,因为getComponent()范围内不会更改指向Component的指针的值。

template <typename T>
T* getComponent() {
    T* component = nullptr;
    retrieveComponent(&component);
    std::cout << std::to_string(component != nullptr) << std::endl;
    return component;
}

template <typename T>
typename std::enable_if<std::is_base_of<Behavior, T>::value>::type
retrieveComponent(T** component) {
    if (components.count(&typeid(BehaviorSet)) == 0) return;
    *component = getComponent<BehaviorSet>()->getBehavior<T>();
}

template <typename T>
typename std::enable_if<!std::is_base_of<Behavior, T>::value>::type
retrieveComponent(T** component) {
    *component = dynamic_cast<T*>(components[&typeid(T)].get());
}

编辑第三次尝试


最后一个版本更时尚。 Daniel Frey指出enable_if有一个模板参数来指定返回类型。然后使用它为T*返回getComponent()非常好!

template <typename T>
typename std::enable_if<std::is_base_of<Behavior, T>::value, T*>::type
getComponent() {
    if (components.count(&typeid(BehaviorSet)) == 0) return nullptr;
    return getComponent<BehaviorSet>()->getBehavior<T>();
}

template <typename T>
typename std::enable_if<!std::is_base_of<Behavior, T>::value, T*>::type
getComponent() {
    if (components.count(&typeid(T)) == 0) return nullptr;
    return dynamic_cast<T*>(components[&typeid(T)].get());
}

<强>结论


我认真考虑做我梦寐以求的实体组件系统架构。但基础框架在这里,所以我现在不会放弃:/