Try*_*yss 14 c++ oop templates design-patterns
假设我有这个课程:
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;
};
Run Code Online (Sandbox Code Playgroud)
我想创建一个具有以下签名的函数:
template<class T> void addElement(T component);
Run Code Online (Sandbox Code Playgroud)
它应该做到以下几点:
component是类型Component1,请将其添加到Component1Listcomponent是类型Component2,添加到Component2List,等等.可能吗?这样做的好方法是什么?
我可以通过以下函数获得相同的行为:
template<class T> void addElement(int componentType, T component);
Run Code Online (Sandbox Code Playgroud)
但我宁愿不必指定componentType这样的:它是无用的信息,它打开了可能的错误的大门(如果componentType不代表组件的类型).
Ric*_*ges 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;
}
Run Code Online (Sandbox Code Playgroud)
Ulr*_*rdt 11
最直接的变体是简单地不使用模板而是重载addElement()函数:
void addElement(Component1 element)
{
this->element1List.push_back(element);
}
void addElement(Component2 element)
{
this->element2List.push_back(element);
}
// ... etc
Run Code Online (Sandbox Code Playgroud)
但是,如果你有很多这样的话,这可能会很乏味(addElement()我猜你不会这样做).使用宏来生成每种类型的代码仍然可以通过合理的努力完成工作.
如果您真的想使用模板,可以使用模板功能并为每种类型专门设置模板功能.但是,与上述方法相比,这并没有减少代码重复的数量.此外,您仍然可以使用宏来生成代码来减少它.
但是,希望以通用方式执行此操作.首先,让我们创建一个包含列表的类型:
template<typename T>
struct ComponentContainer
{
list<T> componentList;
};
Run Code Online (Sandbox Code Playgroud)
现在,派生类只是继承自这个类,并使用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);
}
}
Run Code Online (Sandbox Code Playgroud)
注意:
ComponentContainer是基类,它也没有任何虚函数,甚至也没有虚析构函数.是的,这很危险,应该清楚地记录下来.我不会添加虚拟析构函数,因为它具有开销,因为它不应该被需要.list<T>,也可以从中获取.我没有,因为它会使所有list的成员函数在课堂上可用MyClass(即使不公开),这可能会令人困惑.addElement()函数放入基类模板中以避免派生类中的模板.原因很简单,扫描不同的基类以便执行addElement()功能,然后才执行重载决策.编译器只会addElement()在第一个基类中找到它.