1

仅在GCCClang中测试,基类中存在按值传递的复制赋值运算符(在实现复制和交换(或复制和移动)习语时很有用)导致复制赋值运算符在要隐式删除的派生类。

Clang 和 GCC 对此表示同意;为什么会这样?

示例代码:

#include <string>
#include <iostream>

struct base {
    base() {
        std::cout << "no-arg constructor\n";
    }
    base(const base& other) :
        str{other.str} {
        std::cout << "copy constructor\n";
    }
    base(base&& other) :
        str{std::move(other.str)} {
        std::cout << "move constructor\n";
    }
    base& operator=(base other) {
        std::cout << "copy assigment\n";
        str = std::move(other.str);
        return *this;
    }
    base& operator=(base&& other) {
        std::cout << "move assigment\n";
        str = std::move(other.str);
        return *this;
    }

    std::string str;
};

struct derived : base {
    derived() = default;
    derived(derived&&) = default;
    derived(const derived&) = default;
    derived& operator=(derived&&) = default;
    derived& operator=(const derived&) = default;
};

derived foo() {
    derived ret;
    ret.str = "Hello, world!";
    return ret;
}

int main(int argc, const char* const* argv) {

    derived a;
    a.str = "Wat";
    a = foo(); // foo() returns a temporary - should call move constructor
    return 0;
}
4

2 回答 2

4

在您的代码中,派生的副本分配不会被删除。但是删除的是移动分配,因为[class.copy.assign]/7.4指出,如果基类上移动分配的重载决议不明确,则删除默认的移动分配运算符。

编译器将无法判断是调用operator=(base)还是operator=(base&&)移动基类。


这总是一个问题,即使您尝试将一个基类对象直接分配给另一个基类对象。因此,同时具有两个重载是不切实际的。我不清楚为什么你需要两者。据我所知,您可以消除operator=(base&&)过载而不会产生不良影响。

于 2019-12-21T04:28:45.163 回答
3

[class.copy.assign]/7类的默认复制/移动赋值运算符X定义为已删除,如果X具有:
(7.4) - ...M由于重载决议 (16.3) 而无法复制/移动的直接基类,如应用于 findM的相应赋值运算符,导致歧义......

base的移动分配不明确;它有两个赋值运算符都接受一个右值。请注意,这不会编译

base a, b;
a = std::move(b);

因此,derived最终的移动分配定义为已删除。

于 2019-12-21T04:28:53.927 回答