模板有一种可重用的方法。这是派生类型的通用工厂,带有一个install
和一个create
方法,可以让您编写如下代码:
int main() {
TypeVector<Base> t;
t.install<Foo>("Foo");
t.install<Bar>("Bar");
t.create("Foo")->hello();
}
请注意,这是一个草图实现。在现实世界中,您可以提供另一个模板参数来指定底层容器类型(对于少数类型,vector
可能比 更有效set
)。
类型向量是这样的:
template <typename Base>
class Creator;
template <typename Base>
class TypeVector {
public:
template <typename Derived>
void install (std::string const &name) ;
std::shared_ptr<Base> create (std::string const &name) const;
private:
struct Meta {
Meta(std::shared_ptr<Creator<Base>> creator, std::string const &name)
: creator(creator), name(name) {}
std::shared_ptr<Creator<Base>> creator;
std::string name;
};
std::vector<Meta> creators_;
};
我们不知何故需要一种以可分配方式存储类型的方法。我们这样做boost::shared_ptr
,它结合了一个抽象基类和一个模板派生类:
template <typename Base>
class Creator {
public:
virtual ~Creator() {}
virtual std::shared_ptr<Base> create() const = 0;
};
template <typename Base, typename Derived>
class ConcreteCreator : public Creator<Base> {
public:
virtual std::shared_ptr<Base> create() const {
return std::shared_ptr<Base>{new Derived()};
}
};
“具体的创建者”能够分配一个实际的对象,并返回一个指向它的指针。
最后,这里是TypeVector::install
and的实现TypeVector::create
:
template <typename Base>
template <typename Derived>
void
TypeVector<Base>::install (std::string const &name)
{
creators_.emplace_back(
std::shared_ptr<Creator<Base>>(new ConcreteCreator<Base, Derived>()),
name);
}
template <typename Base>
std::shared_ptr<Base>
TypeVector<Base>::create (std::string const &name) const
{
for (auto m : creators_) {
if (name == m.name) return m.creator->create();
}
throw std::runtime_error("...");
}
最后,这是一个测试:
#include <iostream>
struct Base {
virtual ~Base() {}
virtual void hello() const = 0;
};
struct Foo : Base {
virtual void hello() const { std::cout << "I am a Foo\n"; }
};
struct Bar : Base {
virtual void hello() const { std::cout << "I am a Bar\n"; }
};
int main() {
TypeVector<Base> t;
t.install<Foo>("Foo");
t.install<Bar>("Bar");
t.create("Foo")->hello();
}
你可以走得更远,使任何构造函数都可用于代码,例如...
...
Bar(Color, Age, int)
...
t.create("Foo", Color::Red, Age::TooOld, 42)
... but this requires an awesome grasp of variadic template argument lists, and how to fold them into a constructor call (can be done and has been done, but it would explode this answer).