我遇到了模板问题。
课程Entity
包含std::map
个类型(const std::type_info*
)和Components
(std::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) {}
};
现在主要是我添加BehaviorSet
和CBehavior
:
的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。
非常感谢你的帮助,我希望我的问题很清楚:)
编辑:我认为我已经减少了代码中的所有功能。
答案 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());
}
<强>结论强>
我认真考虑不做我梦寐以求的实体组件系统架构。但基础框架在这里,所以我现在不会放弃:/