具有多重继承的复制赋值运算符

Copy assignment operator with multiple inheritance

提问人:prestokeys 提问时间:11/26/2017 最后编辑:Jodocusprestokeys 更新时间:11/26/2017 访问量:514

问:

我下面的复制构造函数工作正常,但我不明白我的复制分配运算符出了什么问题。

#include <iostream>

template <typename... Ts> class foo;

template <typename Last>
class foo<Last> {
    Last last;
public:
    foo (Last r) : last(r) { }
    foo() = default;
    foo (const foo& other) : last(other.last) { }

    foo& operator= (const foo& other) {
        last = other.last;
        return *this;
    }
};

template <typename First, typename... Rest>
class foo<First, Rest...> : public foo<Rest...> {
    First first;
public:
    foo (First f, Rest... rest) : foo<Rest...>(rest...), first(f) { }
    foo() = default;
    foo (const foo& other) : foo<Rest...>(other), first(other.first) { std::cout << "[Copy constructor called]\n"; }

    foo& operator= (const foo& other) {  // Copy assignment operator
        if (&other == this)
            return *this;
        first = other.first;
        return foo<Rest...>::operator= (other);
    }
};

int main() {
    const foo<int, char, bool> a(4, 'c', true);
    foo<int, char, bool> b = a;  // Copy constructor works fine.
    foo<int, char, bool> c;
//  c = a;  // Won't compile.
}

错误信息:

error: invalid initialization of reference of type 'foo<int, char, bool>&' from expression of type 'foo<char, bool>'
         return foo<Rest...>::operator= (other);
                                              ^

有人可以在这里指出问题吗?

C++ C++11 模板 继承赋 值运算符

评论

0赞 Christian Hackl 11/26/2017
为什么不使用复制和交换?您是否期望出现不可交换或可衡量的性能问题?T

答:

8赞 StoryTeller - Unslander Monica 11/26/2017 #1

您的退货声明

return foo<Rest...>::operator= (other);

返回 a(这是用于定义的引用类型)。但它是从应该返回 .foo<Rest...>operator=foo<First, Rest...>&

从本质上讲,您返回一个需要引用的地方。引用根本不会绑定。BaseDerived&

幸运的是,修复很简单:不要返回 的结果,而是返回。foo<Rest...>::operator=*this

foo& operator= (const foo& other) {  // Copy assignment operator
    if (&other == this)
        return *this;
    first = other.first;
    foo<Rest...>::operator= (other);
    return *this;
}

评论

0赞 prestokeys 11/26/2017
@讲故事的人 是的,确实如此。作为奖励,它被多次调用,但冗余,对吧(它只需要调用一次)?但是没有办法避免这种情况吗?if (&other == this) return *this;
0赞 StoryTeller - Unslander Monica 11/26/2017
@prestokeys - 您始终可以委托给另一个不检查(并且以递归方式实现)的命名成员。operator=
0赞 prestokeys 11/26/2017
@StoryTeller 答案已接受,我实施了您的想法以在单独的答案中对其进行优化。谢谢。
1赞 Nir Friedman 11/26/2017
@prestokeys 实际上,您可能并不想以递归方式实现任何内容。请改用包扩展。在这些情况下可以节省大量的模板膨胀。
1赞 mp31415 11/26/2017 #2

看起来派生类中的返回不正确:operator=

return foo<Rest...>::operator= (other);

它返回基类,而它应该是 .将其更改为*this

this -> foo<Rest...>::operator= (other);
return *this;
0赞 prestokeys 11/26/2017 #3

感谢 StoryTeller,这里有一个优化的完全编译解决方案(operator= 委托给另一个不检查自分配的命名成员名称,并且是递归实现的):copy_data

#include <iostream>

template <typename... Ts> class foo;

template <typename Last>
class foo<Last> {
    Last last;
public:
    foo (Last r) : last(r) { }
    foo() = default;
    foo (const foo& other) : last(other.last) { }

    foo& operator= (const foo& other) {
        if (&other == this)
            return *this;
        last = other.last;
        return *this;
    }
protected:
    void copy_data (const foo& other) {
        last = other.last;
    }
};

template <typename First, typename... Rest>
class foo<First, Rest...> : public foo<Rest...> {
    First first;
public:
    foo (First f, Rest... rest) : foo<Rest...>(rest...), first(f) { }
    foo() = default;
    foo (const foo& other) : foo<Rest...>(other), first(other.first) { std::cout << "[Copy constructor called]\n"; }

    foo& operator= (const foo& other) {  // Copy assignment operator
        if (&other == this)
            return *this;
        first = other.first;
//      foo<Rest...>::operator= (other);
        foo<Rest...>::copy_data(other);
        std::cout << "[Assignment operator called]\n";
        return *this;
    }
protected:
    void copy_data (const foo& other) {
        first = other.first;
        foo<Rest...>::copy_data(other);
    }
};

int main() {
    const foo<int, char, bool> a(4, 'c', true);
    foo<int, char, bool> b = a;  // Copy constructor works fine.
    foo<int, char, bool> c;
    c = b;  // Copy assignment operator works fine (and optimized).
}