Я понимаю, что отвечаю на это пять лет спустя. Возможно, с тех пор язык немного вырос. Я хотел бы предложить что-то, что кажется правильным, если я правильно понимаю вопрос, если только для того, чтобы помочь другим, которые могут найти этот вопрос и задаться вопросом, что они могут сделать.
factory.hpp
#include "base.hpp"
namespace tvr
{
namespace test
{
class factory
{
public:
typedef base::ptr Ptr;
enum eSpecial
{
eDerived
};
template<typename Type>
Ptr create()
{
Ptr result;
result.reset(new Type());
return result;
}
template<typename Type, typename DataType>
Ptr create(const DataType& data)
{
Ptr result;
result.reset(new Type(data));
return result;
}
template<typename Type, typename DataType>
Ptr create(const DataType& data, eSpecial tag)
{
Ptr result;
result.reset(new Type());
static_cast<Type*>(result.get())->set_item(data);
return result;
}
};
}
}
base.hpp
#include <memory>
namespace tvr
{
namespace test
{
class base
{
public:
typedef std::shared_ptr<base> ptr;
public:
base() {}
virtual ~base() {}
virtual void do_something() = 0;
};
}
}
some_class.hpp
#include <ostream>
namespace tvr
{
namespace test
{
struct some_class
{
};
}
}
std::ostream& operator<<(std::ostream& out, const tvr::test::some_class& item)
{
out << "This is just some class.";
return out;
}
template_derived.hpp
#include <iostream>
#include "base.hpp"
namespace tvr
{
namespace test
{
template<typename Type>
class template_derived : public base
{
public:
template_derived(){}
virtual ~template_derived(){}
virtual void do_something()
{
std::cout << "Doing something, like printing _item as \"" << _item << "\"." << std::endl;
}
void set_item(const Type data)
{
_item = data;
}
private:
Type _item;
};
}
}
и, наконец, main.cpp
#include <vector>
#include "base.hpp"
#include "factory.hpp"
namespace tvr
{
namespace test
{
typedef std::vector<tvr::test::base::ptr> ptr_collection;
struct iterate_collection
{
void operator()(const ptr_collection& col)
{
for (ptr_collection::const_iterator iter = col.begin();
iter != col.end();
++iter)
{
iter->get()->do_something();
}
}
};
}
}
#include "template_derived.hpp"
#include "some_class.hpp"
namespace tvr
{
namespace test
{
inline int test()
{
ptr_collection items;
tvr::test::factory Factory;
typedef template_derived<unsigned int> UIntConcrete;
typedef template_derived<double> DoubleConcrete;
typedef template_derived<std::string> StringConcrete;
typedef template_derived<some_class> SomeClassConcrete;
items.push_back(Factory.create<SomeClassConcrete>(some_class(), tvr::test::factory::eDerived));
for (unsigned int i = 5; i < 7; ++i)
{
items.push_back(Factory.create<UIntConcrete>(i, tvr::test::factory::eDerived));
}
items.push_back(Factory.create<DoubleConcrete>(4.5, tvr::test::factory::eDerived));
items.push_back(Factory.create<StringConcrete>(std::string("Hi there!"), tvr::test::factory::eDerived));
iterate_collection DoThem;
DoThem(items);
return 0;
}
}
}
int main(int argc, const char* argv[])
{
tvr::test::test();
}
выход
Doing something, like printing _item as "This is just some class.".
Doing something, like printing _item as "5".
Doing something, like printing _item as "6".
Doing something, like printing _item as "4.5".
Doing something, like printing _item as "Hi there!".
При этом используется комбинация шаблонов, перегрузка функций и разметка перечислений, чтобы помочь создать гибкий фабричный класс, который не требует больших знаний об отдельных классах, которые он создает, для включения шаблонных конкретных классов, о которых спрашивал OP.
Тег 'eDerived' (в форме перечисления) указывает компилятору использовать версию фабричной функции create, которая принимает класс, такой как класс template_derived, у которого есть функция, которая позволяет мне назначать данные одному из его члены. Как вы можете судить по тому, как я заказал заголовки в main.cpp, фабрика ничего не знает о template_derived. Так же как и функция, вызывающая виртуальную функцию базового класса (do_something). Я думаю, что это то, что хотел ОП, но без необходимости добавлять различные функции создания в каждый класс, который может генерировать эта фабрика.
Я также показал, как не нужно явно создавать функции для каждого класса, который должна создавать фабрика. Перегруженные фабрикой функции создания могут создавать все что угодно из базового класса, соответствующего соответствующей сигнатуре.
Я не проводил подробный анализ производительности этого кода, но сделал достаточно, чтобы убедиться, что большая часть работы выполняется в операторе потоковой передачи. Это компилируется примерно за 1 секунду на моем четырехъядерном компьютере с частотой 3,30 ГГц. Возможно, вам придется поэкспериментировать с более надежным кодом, чтобы увидеть, насколько сильно он может затормозить компилятор, если вообще сильно.
Я тестировал этот код в VC ++ 2015, хотя, вероятно, он довольно легко работает в других компиляторах. Если вы хотите скопировать это, вам нужно добавить свои собственные защитные заголовки. В любом случае, я надеюсь, что это полезно.