This sounds like a lazy factory pattern. As you say in a comment, Constr<Base>
does not hold enough information to construct a Derived
. So when storing in a vector, you should use pointers rather than values. If you are willing to use the free store for the final objects, one solution is the following:
struct Base { int x; };
struct Derived : public Base { Derived(int x) : Base{x} { } };
template<typename T>
struct Constr;
template<>
struct Constr<Base> {
virtual std::unique_ptr<Base> make() const = 0;
};
template<typename T>
struct Constr : Constr<Base> {
int arg;
Constr(int arg) : arg{arg} { }
std::unique_ptr<Base> make() const override
{
return std::make_unique<T>(arg);
}
};
int main() {
Constr<Derived> a{5}, b{2}, c{8};
std::vector<Constr<Base>*> v{&a, &b, &c};
// later...
std::vector<std::unique_ptr<Base>> u;
for (auto& i : v)
u.push_back(i->make());
for (auto& i : u)
std::cout << i->x << " "; // 5 2 8
}
For this to work, we now make Constr<Derived>
derive Constr<Base>
. So we can use pointers/references to such objects following the same rules as pointers/references to Base
/Derived
, without defining any special constructors.
Note the forward declaration of Constr
: this is needed because specialization Const<Base>
needs to be defined before the general template; in turn, this is needed because the latter implicitly instantiates the former (by deriving it).
I cannot think of a simple solution without the free store, though. The only possibility is to use a raw memory buffer, either as a data member of Constr<Base>
, or passed as an argument to make()
. But this needs some work and will always have an upper bound on the size of Derived
. Or, we could automatically choose between stack/free store depending on object size; this is generic but needs even more work.