移动分配与标准复制和交换不兼容
Move Assignment incompatible with Standard Copy and Swap
测试新的移动语义。
我只是问了我在移动构造函数上遇到的一个问题。但正如评论中发现的那样,问题实际上是当您使用标准的"复制和交换"习惯用法时,"移动分配"运算符和"标准分配"运算符会发生冲突。
这是我正在使用的类:
#include <string.h>
#include <utility>
class String
{
int len;
char* data;
public:
// Default constructor
// In Terms of C-String constructor
String()
: String("")
{}
// Normal constructor that takes a C-String
String(char const* cString)
: len(strlen(cString))
, data(new char[len+1]()) // Allocate and zero memory
{
memcpy(data, cString, len);
}
// Standard Rule of three
String(String const& cpy)
: len(cpy.len)
, data(new char[len+1]())
{
memcpy(data, cpy.data, len);
}
String& operator=(String rhs)
{
rhs.swap(*this);
return *this;
}
~String()
{
delete [] data;
}
// Standard Swap to facilitate rule of three
void swap(String& other) throw ()
{
std::swap(len, other.len);
std::swap(data, other.data);
}
// New Stuff
// Move Operators
String(String&& rhs) throw()
: len(0)
, data(null)
{
rhs.swap(*this);
}
String& operator=(String&& rhs) throw()
{
rhs.swap(*this);
return *this;
}
};
我认为相当沼泽标准。
然后我像这样测试我的代码:
int main()
{
String a("Hi");
a = String("Test Move Assignment");
}
在这里,a
的赋值应使用"移动赋值"运算符。但是与"标准分配"运算符(编写为标准复制和交换)存在冲突。
> g++ --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include/c++/4.2.1
Apple LLVM version 5.0 (clang-500.2.79) (based on LLVM 3.3svn)
Target: x86_64-apple-darwin13.0.0
Thread model: posix
> g++ -std=c++11 String.cpp
String.cpp:64:9: error: use of overloaded operator '=' is ambiguous (with operand types 'String' and 'String')
a = String("Test Move Assignment");
~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
String.cpp:32:17: note: candidate function
String& operator=(String rhs)
^
String.cpp:54:17: note: candidate function
String& operator=(String&& rhs)
^
现在,我可以通过将"标准分配"运算符修改为:
String& operator=(String const& rhs)
{
String copy(rhs);
copy.swap(*this);
return *this;
}
但这并不好,因为它会破坏编译器优化复制和交换的能力。请参阅什么是复制和交换成语?这里和这里
我错过了一些不那么明显的东西吗?
如果将赋值运算符定义为取值,则不应(不需要也不能)定义采用右值引用的赋值运算符。这毫无意义。
通常,当您需要区分左值和右值时,您只需要提供采用右值引用的重载,但在这种情况下,您选择的实现意味着您不需要进行这种区分。无论您有左值还是右值,您都将创建参数并交换内容。
String f();
String a;
a = f(); // with String& operator=(String)
在这种情况下,编译器将解析调用a.operator=(f());
它将意识到返回值的唯一原因是要operator=
的参数,并将省略任何副本 - 这是首先使函数取值的重点!
其他答案建议只有一个重载operator =(String rhs)
按值获取参数,但这不是最有效的实现。
的确,在大卫·罗德里格斯的这个例子中 - 德里贝亚斯
String f();
String a;
a = f(); // with String& operator=(String)
不制作副本。但是,假设只提供了operator =(String rhs)
并考虑以下示例:
String a("Hello"), b("World");
a = b;
发生的情况是
-
b
复制到rhs
(内存分配 +memcpy
); - 交换
a
和rhs
; -
rhs
被摧毁。
如果我们实现operator =(const String& rhs)
和operator =(String&& rhs)
那么当目标的长度大于源的长度时,我们可以避免步骤 1 中的内存分配。例如,这是一个简单的实现(不完美:如果String
有一个capacity
成员可能会更好):
String& operator=(const String& rhs) {
if (len < rhs.len) {
String tmp(rhs);
swap(tmp);
else {
len = rhs.len;
memcpy(data, rhs.data, len);
data[len] = 0;
}
return *this;
}
String& operator =(String&& rhs) {
swap(rhs);
}
除了性能点,如果swap
是noexcept
,那么operator =(String&&)
也可以noexcept
。(如果"可能"执行内存分配,则情况并非如此。
在霍华德·欣南特(Howard Hinnant)的精彩解释中查看更多详细信息。
复制和分配所需的只是:
// As before
String(const String& rhs);
String(String&& rhs)
: len(0), data(0)
{
rhs.swap(*this);
}
String& operator = (String rhs)
{
rhs.swap(*this);
return *this;
}
void swap(String& other) noexcept {
// As before
}
- 我收到同义重复编译器错误。我应该如何修复"类型"X"的参数与类型"X"的参数不兼容?
- 字符类型转换不兼容
- Qt:如何使不兼容的发送方/接收方参数兼容?
- 视觉工作室 2017;启用 /permissive 时,类型 "const wchar_t *" 的参数与类型 "PWSTR" 的参数不兼容
- 使用不兼容的分配器复制分配无序列图
- 类型为 "int*" 的参数与 C++ 中错误类型"int**"参数不兼容
- 该对象具有与成员函数不兼容的类型限定符.为什么会出现此错误?
- 我正在尝试将表的地址传递给要在另一个函数中使用的指针,但得到不兼容的指针类型
- 为什么范围算法与 std 的迭代器不兼容?
- Winpcap Findalldevs const char * 与 char * 不兼容
- C++ 类型的参数与 void (__cdecl*)(void) 类型的参数不兼容,当调用 std::atexit()
- 将"std::string {aka std::basic_string}"赋值中的不兼容类型<char>
- 如何在C++中停止调用不兼容的方法?
- OPENCL 警告:不兼容的指针类型将'float __global[16]'传递给类型为 '__global float4 的参数 *
- 函数范围的静态变量如何导致与共享库中函数代码的未来使用不兼容
- 如何修复"方法的类型与 PInvoke 不兼容"
- C++不兼容的迭代器类型
- 编译器错误"在if语句中分配不兼容的类型"
- 程序无法编译:将 'int (*)[3][3]' 赋值为 'int [9][3][3]' 中的不兼容类型
- 移动分配与标准复制和交换不兼容