将模板<derived>分配给模板<base />变量</derived>

时间:2014-05-06 10:43:21

标签: c++ templates c++11 vector

我有一个模板类,它可以替换对象的直接构造,只是存储它们以备将来使用。

template<typename T>
typedef struct Construction{
     int arg;
} Construction;

现在我想将std::vector< Construction< Base > >传递给一个函数,然后该函数应该使用给定的参数调用每个Element的构造函数。

当我只使用基类型时,这种方法有效,但只要我尝试混合基类型和派生类型,就会出现问题:

Construction<Base> construct = Construction<Derived>(5);

这不会奏效,而像Base& b = Derived(5);这样的常规作业确实如此。

为什么这不起作用?

2 个答案:

答案 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大小的上限。或者,我们可以根据对象大小自动选择堆栈/免费存储;这是通用的,但需要更多的工作。