假设我有这个课程:
class Component1; class Component2; // many different Components class Component42; class MyClass { public: MyClass(void) {}; std::listcomponent1List; std::list component2List; // one list by component std::list component42List; };
我想创建一个具有以下签名的函数:
templatevoid addElement(T component);
它应该做到以下几点:
如果component
是类型Component1
,请将其添加到Component1List
if component
是类型Component2
,添加到Component2List
,等等.
可能吗?这样做的好方法是什么?
我可以通过以下函数获得相同的行为:
templatevoid addElement(int componentType, T component);
但我宁愿不必指定componentType
这样的:它是无用的信息,它打开了可能的错误的大门(如果componentType
不代表组件的类型).
std::tuple
救援.
使用 std::decay_t
添加了可变参数形式
add_component()
现在返回对此的引用以允许调用链接.
#include#include #include
#include #include class Component1 {}; class Component2 {}; struct Component3 { Component3() {} }; // many different Components template class MyClassImpl { template using list_of = std::list ; public: using all_lists_type = std::tuple< list_of ... >; // add a single component template MyClassImpl& add_component(Component&& c) { list_for ().push_back(std::forward (c)); return *this; } // add any number of components template MyClassImpl& add_components(Components&&... c) { using expand = int[]; void(expand { 0, (void(add_component(std::forward (c))), 0)... }); return *this; } template auto& list_for() { using component_type = std::decay_t ; return std::get >(_lists); } template const auto& list_for() const { using component_type = std::decay_t ; return std::get >(_lists); } private: all_lists_type _lists; }; using MyClass = MyClassImpl ; 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 ().size() << std::endl; return 0; }
最直接的变体是简单地不使用模板而是重载addElement()
函数:
void addElement(Component1 element) { this->element1List.push_back(element); } void addElement(Component2 element) { this->element2List.push_back(element); } // ... etc
但是,如果你有很多这样的话,这可能会很乏味(addElement()
我猜你不会这样做).使用宏来生成每种类型的代码仍然可以通过合理的努力完成工作.
如果您真的想使用模板,可以使用模板功能并为每种类型专门设置模板功能.但是,与上述方法相比,这并没有减少代码重复的数量.此外,您仍然可以使用宏来生成代码来减少它.
但是,希望以通用方式执行此操作.首先,让我们创建一个包含列表的类型:
templatestruct ComponentContainer { list componentList; };
现在,派生类只是继承自这个类,并使用C++类型系统来定位正确的容器基类:
class MyClass: ComponentContainer, ComponentContainer , ComponentContainer { public: template void addElement(T value) { ComponentContainer & container = *this; container.componentList.push_back(value); } }
注意:
这使用私有继承,这与您最初使用的包含非常相似.
即使ComponentContainer
是基类,它也没有任何虚函数,甚至也没有虚析构函数.是的,这很危险,应该清楚地记录下来.我不会添加虚拟析构函数,因为它具有开销,因为它不应该被需要.
您可以完全放弃中间容器list
,也可以从中获取.我没有,因为它会使所有list
的成员函数在课堂上可用MyClass
(即使不公开),这可能会令人困惑.
您不能将该addElement()
函数放入基类模板中以避免派生类中的模板.原因很简单,扫描不同的基类以便执行addElement()
功能,然后才执行重载决策.编译器只会addElement()
在第一个基类中找到它.
这是一个简单的C++ 98解决方案,对于C++ 11,我会看一下Jens和Richard建议的基于类型的元组查找解决方案.