Mar*_*ork 26 c++ move-semantics copy-and-swap c++11
测试新的Move Semantics.
我刚刚问了一下移动构造函数我遇到的问题.但是,正如评论中所述,问题实际上是"移动分配"操作符和"标准分配"操作符在使用标准的"复制和交换"惯用法时发生冲突.
这是我正在使用的课程:
#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;
}
};
Run Code Online (Sandbox Code Playgroud)
我觉得漂亮的沼气标准.
然后我像这样测试了我的代码:
int main()
{
String a("Hi");
a = String("Test Move Assignment");
}
Run Code Online (Sandbox Code Playgroud)
这里的赋值a应使用"Move Assignment"运算符.但是与"标准作业"操作符发生冲突(作为标准副本和交换编写).
> 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)
^
Run Code Online (Sandbox Code Playgroud)
现在我可以通过修改"标准分配"操作符来解决这个问题:
String& operator=(String const& rhs)
{
String copy(rhs);
copy.swap(*this);
return *this;
}
Run Code Online (Sandbox Code Playgroud)
但这并不好,因为它混淆了编译器优化复制和交换的能力.请参阅什么是复制和交换习惯用法?在这里和这里
我错过了一些不太明显的东西吗?
Dav*_*eas 25
如果定义赋值运算符以获取值,则不应(不需要也不能)定义采用rvalue-reference的赋值运算符.没有意义.
通常,当您需要区分左值和右值时,您只需要提供一个带右值引用的重载,但在这种情况下,您选择的实现意味着您不需要进行区分.无论您是左值还是左值,您都要创建参数并交换内容.
String f();
String a;
a = f(); // with String& operator=(String)
Run Code Online (Sandbox Code Playgroud)
在这种情况下,编译器将解析调用,a.operator=(f());它将意识到返回值的唯一原因是作为参数operator=并且将忽略任何副本 - 这是使函数在第一时间取值的关键!
其他答案建议只有一个重载operator =(String rhs)按值来获取参数,但这不是最有效的实现.
确实,在这个例子中大卫罗德里格斯 - 运动员
String f();
String a;
a = f(); // with String& operator=(String)
Run Code Online (Sandbox Code Playgroud)
没有复制.但是,假设只operator =(String rhs)提供并考虑此示例:
String a("Hello"), b("World");
a = b;
Run Code Online (Sandbox Code Playgroud)
会发生什么
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);
}
Run Code Online (Sandbox Code Playgroud)
此外,如果性能点swap是noexcept,那么operator =(String&&)可以noexcept为好.(如果"潜在"执行内存分配,则不是这种情况.)
在Howard Hinnant的这个出色的解释中查看更多细节.
| 归档时间: |
|
| 查看次数: |
3995 次 |
| 最近记录: |