为什么基 class 中的复制和交换会导致复制赋值运算符在派生 class 中被隐式删除?
Why does copy-and-swap in a base class cause the copy-assignment operator to be implicitly deleted in the derived class?
仅在 GCC and Clang 中测试过,在基 class 中存在按值传递复制赋值运算符(在实现复制和交换(或复制和- move) 成语) 导致派生的 class 中的复制赋值运算符被隐式删除。
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;
}
在您的代码中,未删除派生的复制分配。但是删除的是移动赋值,因为 [class.copy.assign]/7.4,它声明如果基 class 上的移动赋值的重载决策不明确,则删除默认的移动赋值运算符。
编译器无法判断是调用 operator=(base)
还是 operator=(base&&)
来移动基数 class.
这始终是个问题,即使您尝试将一个基础 class 对象直接移动到另一个基础 class 对象。所以同时拥有两个重载是不切实际的。我不清楚为什么你需要两者。据我所知,您可以删除 operator=(base&&)
过载而不会产生不良影响。
[class.copy.assign]/7 A defaulted copy/move assignment operator for class X
is defined as deleted if X
has:
(7.4) - ...a direct base class M
that cannot be copied/moved because overload resolution (16.3), as applied to find M
’s corresponding assignment operator, results in an ambiguity...
base
的移动分配不明确;它有两个赋值运算符都接受一个右值。观察这个 doesn't compile:
base a, b;
a = std::move(b);
因此,derived
的移动分配最终定义为已删除。
仅在 GCC and Clang 中测试过,在基 class 中存在按值传递复制赋值运算符(在实现复制和交换(或复制和- move) 成语) 导致派生的 class 中的复制赋值运算符被隐式删除。
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;
}
在您的代码中,未删除派生的复制分配。但是删除的是移动赋值,因为 [class.copy.assign]/7.4,它声明如果基 class 上的移动赋值的重载决策不明确,则删除默认的移动赋值运算符。
编译器无法判断是调用 operator=(base)
还是 operator=(base&&)
来移动基数 class.
这始终是个问题,即使您尝试将一个基础 class 对象直接移动到另一个基础 class 对象。所以同时拥有两个重载是不切实际的。我不清楚为什么你需要两者。据我所知,您可以删除 operator=(base&&)
过载而不会产生不良影响。
[class.copy.assign]/7 A defaulted copy/move assignment operator for class
X
is defined as deleted ifX
has:
(7.4) - ...a direct base classM
that cannot be copied/moved because overload resolution (16.3), as applied to findM
’s corresponding assignment operator, results in an ambiguity...
base
的移动分配不明确;它有两个赋值运算符都接受一个右值。观察这个 doesn't compile:
base a, b;
a = std::move(b);
因此,derived
的移动分配最终定义为已删除。