假设我有这门课程:
class Component1;
class Component2;
// many different Components
class Component42;
class MyClass
{
public:
MyClass(void) {};
std::list<Component1> component1List;
std::list<Component2> component2List;
// one list by component
std::list<Component42> component42List;
};
我想创建一个具有以下签名的函数:
template<class T> void addElement(T component);
应该执行以下操作:
component
的类型为Component1
,请将其添加到Component1List
component
属于Component2
类型,请将其添加到Component2List
等。有可能吗?有什么好办法呢?
我可以通过以下函数获得相同的行为:
template<class T> void addElement(int componentType, T component);
但我不必像这样指定componentType
:它是无用的信息,它为可能的错误打开了大门(如果componentType
不代表组件的类型)。< / p>
答案 0 :(得分:13)
std::tuple
救援。
使用std::decay_t
添加了可变参数形式
add_component()
现在返回对此的引用以允许调用链接。
#include <iostream>
#include <list>
#include <utility>
#include <type_traits>
#include <tuple>
class Component1 {};
class Component2 {};
struct Component3 {
Component3() {}
};
// many different Components
template<class...ComponentTypes>
class MyClassImpl
{
template<class Component> using list_of = std::list<Component>;
public:
using all_lists_type =
std::tuple<
list_of<ComponentTypes> ...
>;
// add a single component
template<class Component>
MyClassImpl& add_component(Component&& c)
{
list_for<Component>().push_back(std::forward<Component>(c));
return *this;
}
// add any number of components
template<class...Components>
MyClassImpl& add_components(Components&&... c)
{
using expand = int[];
void(expand { 0, (void(add_component(std::forward<Components>(c))), 0)... });
return *this;
}
template<class Component>
auto& list_for()
{
using component_type = std::decay_t<Component>;
return std::get<list_of<component_type>>(_lists);
}
template<class Component>
const auto& list_for() const
{
using component_type = std::decay_t<Component>;
return std::get<list_of<component_type>>(_lists);
}
private:
all_lists_type _lists;
};
using MyClass = MyClassImpl<Component1, Component2, Component3>;
int main()
{
MyClass c;
c.add_component(Component1());
c.add_component(Component2());
const Component3 c3;
c.add_component(c3);
c.add_components(Component1(),
Component2(),
Component3()).add_components(Component3()).add_components(Component1(),
Component2());
std::cout << c.list_for<Component1>().size() << std::endl;
return 0;
}
答案 1 :(得分:11)
最直接的变体是不使用模板而是重载addElement()
函数:
void addElement(Component1 element)
{
this->element1List.push_back(element);
}
void addElement(Component2 element)
{
this->element2List.push_back(element);
}
// ... etc
但是,如果你有很多这样的话,这可能会很乏味(我猜你不只是addElement()
)。使用宏来为每种类型生成代码仍然可以通过合理的努力来完成工作。
如果您真的想使用模板,可以使用模板功能并为每种类型专门设置模板功能。但是,与上述方法相比,这并没有减少代码重复的数量。此外,您仍然可以使用宏来生成代码来减少它。
然而,希望以通用的方式做到这一点。首先,让我们创建一个包含列表的类型:
template<typename T>
struct ComponentContainer
{
list<T> componentList;
};
现在,派生类只是继承自这个类,并使用C ++类型系统来定位正确的容器基类:
class MyClass:
ComponentContainer<Component1>,
ComponentContainer<Component2>,
ComponentContainer<Component3>
{
public:
template<typename T>
void addElement(T value)
{
ComponentContainer<T>& container = *this;
container.componentList.push_back(value);
}
}
请注意:
ComponentContainer
是基类,它也没有任何虚函数,甚至没有虚析构函数。是的,这很危险,应该清楚地记录下来。我不会添加虚拟析构函数,因为它有开销,因为它不需要。list<T>
派生。我之所以没有,是因为它会使list
类MyClass
的所有成员函数都可用(即使不是公开的),这可能会令人困惑。addElement()
函数放入基类模板中以避免派生类中的模板。原因很简单,为了addElement()
函数扫描不同的基类,然后才执行重载决策。因此编译器只会在第一个基类中找到addElement()
。答案 2 :(得分:2)
如果没有太多课程,你可以使用重载。可以使用基于类型的元组查找来完成基于模板的解决方案:
class MyClass {
public:
template<typename T> void addElement(T&& x) {
auto& l = std::get<std::list<T>>(lists);
l.insert( std::forward<T>(x) );
}
private:
std::tuple< std::list<Component1>, std::list<Component2> > lists;
};
答案 3 :(得分:1)
如果您事先不知道在实例化多容器时需要存储的类型,则选项是隐藏类型并使用type_index
保留列表地图:
struct Container {
struct Entry {
void *list;
std::function<void *(void*)> copier;
std::function<void(void *)> deleter;
};
std::map<std::type_index, Entry> entries;
template<typename T>
std::list<T>& list() {
Entry& e = entries[std::type_index(typeid(T))];
if (!e.list) {
e.list = new std::list<T>;
e.deleter = [](void *list){ delete ((std::list<T> *)list); };
e.copier = [](void *list){ return new std::list<T>(*((std::list<T> *)list)); };
}
return *((std::list<T> *)e.list);
}
~Container() {
for (auto& i : entries) i.second.deleter(i.second.list);
}
Container(const Container& other) {
// Not exception safe... se note
for (auto& i : other.entries) {
entries[i.first] = { i.second.copier(i.second.list),
i.second.copier,
i.second.deleter };
}
};
void swap(Container& other) { std::swap(entries, other.entries); }
Container& operator=(const Container& other) {
Container(other).swap(*this);
return *this;
};
Container() { }
};
可以用作:
Container c;
c.list<int>().push_back(10);
c.list<int>().push_back(20);
c.list<double>().push_back(3.14);
注意:现在写的复制构造函数不是异常安全的,因为如果copier
抛出(由于内存不足或因为列表中元素的复制构造函数抛出),已经分配的列表将会不能解除分配。
答案 4 :(得分:-1)
void addElement(Component1 component) {
componentList1.insert(component);
}
void addElement(Component2 component) {
componentList2.insert(component);
}