C++, std::list, 赋值, 继承

C++, std::list, assignment, inheritance

class A, B;
class A {
    public:
        A& operator= ( const A &rhs ) { return *this; }
};
class B: public A {
    public:
        B& operator= ( const A &rhs ) { return *this; }
};
A a;
B b;
std::list < A > aa;
std::list < B > bb;
a = b; // works
b = a; // works
// aa = bb; // fails
// bb = aa; // fails

如何让 bb = aa 起作用?

您可以将 A 类型的对象分配给 B 类型的对象并不意味着 list<A>list<B> 也适用。

您可以使用 std::copy:

std::copy(bb.begin(), bb.end(), std::back_inserter(aa)); // instead of aa = bb

编辑:为了使用它,您必须先调用 aa.resize(bb.size()),或者最好使用 @Christophe 指出的 back_inserter


你应该清楚你在这里做什么:它要么切片(A = B)要么分配一个非最终的class(B = A)。为了避免这种情况,您应该使用指针并使用克隆模式。

您可以使用 copy and a back_inserter 来做到这一点:

std::copy(aa.begin(), aa.end(), back_inserter<list<B>>(bb));

但前提是目标元素(这里是B)可以从源元素(这里是A)构造。所以在这里,您需要一个基于 A 对象的 B 构造函数。

Online demo

注意:如果你没有目标中复制所需的构造函数class,但你有一些其他方法从源构建目标对象,你可以考虑使用std::transform()

顺便说一下,注意:a=b 有效,但可能会导致 slicing

就编译器而言,std::list<A>std::list<B> 是不相交的类型。如果您认为 std::list 已在内部为 A 对象分配内存,那么这是有道理的。尝试将 B 个对象分配给 space 可能是灾难性的。

想象一下,例如,B 有一个额外的 属性。现在,如果您试图将 B 存储到足以容纳 A 的内存中,它可能不适合。可以使用类似的逻辑来查看为什么另一个方向也失败了:如果将 A 存储到 space 中以获得 B,编译器期望额外的 属性 B 它认为 space 是有效的。但是,如果您分配了一个 A,那么 space 中有谁知道其中的内容。

如果您希望此作业能够工作,您将需要使用某种形式的间接寻址。例如,您可以使用两个 std::list<A*> 实例或两个 std::list<std::shared_ptr<A>> 实例。这样就可以工作了,因为 B* 可以 被安全地视为 A*,至少假设 类 被正确写入。

你在这里遗漏的是,即使 AB 是相关类型,std::list<A>std::list<B> 实际上是不相关的类型(除了都说list)。因此,您不能使用复制分配在它们之间进行分配。

但是,假设您可以使用赋值运算符将类型 AB 相互分配,您可以使用 listassign复制迭代器范围的方法:

aa.assign(bb.begin(), bb.end());
bb.assign(aa.begin(), aa.end());

我认为没有人真正尝试回答如何对 bb = aa;

进行赋值的问题

如前所述,您不能将 std::list<B> 的隐式强制转换或赋值运算符定义为自由运算符。但你有两个选择。他们都依赖于 subclassing 这个集合...只要你不允许任何额外的状态就可以。

选项 #1,是将 BB 定义为 std::list<B> 的子 class,而 bb 将使用这些 class。 (BB 会得到一个额外的赋值运算符。)

选项 #2,是使用助手 class,并且可能更接近您的初衷。助手 class 的定义类似于上述 BB class,将隐式转换运算符添加回 std::list<B>.

完整示例如下:

class A {};

class B: public A {
public:
    B() = default;
    B( const A &rhs ) {} // Empty because A and B are not defined with any state.
    B& operator= ( const A &rhs ) { return *this; }
};

class BB : public std::list<B> {
public:
    BB() = default;
    BB(const std::list<A> &aa) { assign( aa.begin(), aa.end() ); }
    BB& operator= ( const std::list<A> &rhs ) { assign(rhs.begin(), rhs.end()); return *this; }
    operator std::list<B>() { return *this; }
};
// No new member variables or virtual methods are allowed.
static_assert( sizeof (std::list<B>) == sizeof (BB), "Bad derivation of std::list<B>" );


A a;
B b;

b = a; // works

std::list<A> aa;
std::list<B> bb;

BB helper;
bb = helper = aa;  // Option #2; bb is std::list<B>

或者您可以直接使用 BB:

BB bb = aa;        // Option #1; use bb just like std::list<B>