我有一个模板类,它可以替换对象的直接构造,只是存储它们以备将来使用。
template<typename T>
typedef struct Construction{
int arg;
} Construction;
现在我想将std::vector< Construction< Base > >
传递给一个函数,然后该函数应该使用给定的参数调用每个Element的构造函数。
当我只使用基类型时,这种方法有效,但只要我尝试混合基类型和派生类型,就会出现问题:
Construction<Base> construct = Construction<Derived>(5);
这不会奏效,而像Base& b = Derived(5);
这样的常规作业确实如此。
为什么这不起作用?
答案 0 :(得分:3)
它不起作用,因为类模板的实例化不通过子类 - 超类关系相关,即使实例化中使用的类型也是如此。
在许多情况下,您可以通过向原始模板提供构造函数模板和/或赋值运算符模板来解决问题,例如:
struct Base {
int x;
};
struct Derived : public Base {
int y;
};
template<typename T>
struct Constr {
T t;
Constr() = default;
template<typename U> Constr(const Constr<U> &a) : t(a.t) {}
};
int main() {
Constr<Derived> a;
Constr<Base> b = a;
// Constr<Derived> c = b; // error, as it should
}
答案 1 :(得分:2)
这听起来像一个懒惰的工厂模式。正如您在评论中所说,Constr<Base>
没有足够的信息来构建Derived
。因此,当存储在向量中时,您应该使用指针而不是值。如果您愿意将免费商店用于最终对象,可以采用以下解决方案:
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
}
为此,我们现在将Constr<Derived>
派生 Constr<Base>
。所以我们可以使用与Base
/ Derived
的指针/引用相同的规则来指向这些对象的指针/引用,而不定义任何特殊的构造函数。
请注意Constr
的前向声明:这是必需的,因为需要在通用模板之前定义专门化Const<Base>
;反过来,这是必要的,因为后者隐含地实例化前者(通过推导它)。
Constr<Base>
的数据成员,或作为参数传递给make()
。但这需要一些工作,并且总是会有Derived
大小的上限。或者,我们可以根据对象大小自动选择堆栈/免费存储;这是通用的,但需要更多的工作。