为什么基类中的复制和交换会导致派生类中的复制赋值运算符被隐式删除?



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

Clang和GCC同意这一点;为什么会这样呢?

示例代码:

#include <string>
#include <iostream>
struct base {
base() {
std::cout << "no-arg constructorn";
}
base(const base& other) :
str{other.str} {
std::cout << "copy constructorn";
}
base(base&& other) :
str{std::move(other.str)} {
std::cout << "move constructorn";
}
base& operator=(base other) {
std::cout << "copy assigmentn";
str = std::move(other.str);
return *this;
}
base& operator=(base&& other) {
std::cout << "move assigmentn";
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;
}

在代码中,不会删除派生的副本赋值。但是,由于 [class.copy.assign]/7.4,删除的是移动赋值,其中指出,如果基类上移动赋值的重载解析不明确,则会删除默认的移动赋值运算符。

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


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

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

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

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

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

最新更新