提问人:SumDood 提问时间:12/21/2019 更新时间:12/26/2019 访问量:149
为什么基类中的复制和交换会导致在派生类中隐式删除复制赋值运算符?
Why does copy-and-swap in a base class cause the copy-assignment operator to be implicitly deleted in the derived class?
问:
仅在 GCC 和 Clang 中测试,基类中存在按值传递的复制赋值运算符(在实现复制和交换(或复制和移动)惯用语时很有用)会导致派生类中的复制赋值运算符被隐式删除。
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赞
walnut
12/21/2019
#1
在代码中,不会删除派生的副本赋值。但是,由于 [class.copy.assign]/7.4 的原因,删除的是移动赋值,该赋值指出,如果基类上移动赋值的重载解析不明确,则删除默认的移动赋值运算符。
编译器无法判断是调用还是移动基类。operator=(base)
operator=(base&&)
这始终是一个问题,即使您尝试将一个基类对象直接分配给另一个基类对象也是如此。因此,同时具有两个重载并不实际。我不清楚为什么两者都需要。据我所知,您可以消除过载而不会产生不良影响。operator=(base&&)
3赞
Igor Tandetnik
12/21/2019
#2
[类.copy.assign]/7如果具有以下条件,则类的默认复制/移动赋值运算符被定义为已删除:
(7.4) - ...无法复制/移动的直接基类,因为应用于 find 的相应赋值运算符的重载解析 (16.3) 会导致歧义......X
X
M
M
base
的移动分配模棱两可;它有两个赋值运算符,都接受 rvalue。请注意,这不会编译:
base a, b;
a = std::move(b);
因此,最终的移动分配被定义为已删除。derived
评论
1赞
M.M
12/21/2019
同样相关的是“定义为已删除的默认移动赋值运算符被重载解析忽略”,因此 OP 的赋值语句使用复制赋值运算符,即使移动更匹配
评论
operator=(base other)
不是复制赋值运算符,它只是一个常规赋值运算符。复制赋值运算符仅作为参数。base const&
derived& operator=(derived&& other)
base::operator=(std::move(other))
base