C++、std::列表、赋值、继承

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

提问人:Y. L. 提问时间:4/17/2016 最后编辑:PrixY. L. 更新时间:4/18/2016 访问量:445

问:

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 工作?

C++ 继承 赋值运算符 stdlist

评论


答:

3赞 davidhigh 4/17/2016 #1

您可以将类型的对象分配给类型的对象并不意味着 for 和 也成立。ABlist<A>list<B>

代替你可以使用:std::copy

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

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


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

4赞 Christophe 4/17/2016 #2

您可以使用副本back_inserter来执行此操作:

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

但条件是目标元素(此处为 B)可以从源元素(此处为 A)构造。所以在这里,你需要一个基于 A 对象的 B 构造函数。

在线演示

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

顺便说一句,注意:有效,但可能会导致切片a=b

评论

2赞 Mark B 4/17/2016
只使用复制插入器模式呢?std::list::assign(iter, iter)
0赞 Christophe 4/17/2016
太好了!我错误地认为范围必须是相同类型的列表(此外,我喜欢插入器)。你应该提供这个答案,这样我们就可以投赞成票了! .
2赞 Corbin 4/17/2016 #3

就编译器而言,并且是不相交的类型。如果您认为它已为对象分配了内部内存,则这是有道理的。尝试将对象分配到该空间中可能是灾难性的。std::list<A>std::list<B>std::listAB

例如,假设 B 有一个额外的属性。现在,如果您尝试将 存储到足够大的内存中,那么它可能不适合。类似的逻辑可以用来查看为什么另一个方向也失败了:如果你将一个 存储到 空间 ,编译器期望它认为在该空间的额外属性是有效的。如果你分配了一个,那么这个空间里有谁知道什么。BAABBA

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

评论

0赞 geipel 4/17/2016
您的所有积分都是有效的。这是我读到这个问题时的第一个想法。但是,由于作者确实为 定义了一个赋值运算符,因此即使按值计算,使用也是可以接受的。b=astd::list<B>
0赞 Corbin 4/17/2016
@geipel 是的,但这并不能真正帮助他将 a 分配给 .这只是意味着他可以将实例分配到 .我假设他的意图是避免任何他基本上必须重新创建列表来分配它的事情。std::list<B>std::list<A>Astd::list<B>
0赞 geipel 4/17/2016
真。但幸运的是,最初的问题是关于开始工作——即从 .在这种特定情况下,这是一个有效的问题。bb = aastd::list<B>std::list<A>
6赞 Mark B 4/17/2016 #4

你在这里缺少的是,即使 和 是相关的类型,并且实际上是不相关的类型(除了两者都说)。因此,您不能使用副本分配在它们之间进行分配。ABstd::list<A>std::list<B>list

但是,假设您可以使用它们的赋值运算符将类型和类型相互赋值,则可以使用 的方法复制迭代器的范围:ABlistassign

aa.assign(bb.begin(), bb.end());
bb.assign(aa.begin(), aa.end());
1赞 geipel 4/17/2016 #5

我不认为有人真的试图回答如何完成分配的问题bb = aa;

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

选项 #1 是定义为 的子类,并将使用这些类。( 将获得一个额外的赋值运算符。BBstd::list<B>bbBB

选项 #2 是使用帮助程序类,并且可能更接近您的原始意图。帮助程序类的定义与上面描述的类类似,将隐式转换运算符添加回 。BBstd::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>