移动分配与标准复制和交换不兼容 [英] Move Assignment incompatable with Standard Copy and Swap

查看:156
本文介绍了移动分配与标准复制和交换不兼容的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

测试新的移动语义。



我刚刚问了我使用移动构造函数的问题。但是,事实证明,在评论中的问题是,当你使用标准的复制和交换成语时,移动分配操作符和标准分配操作符冲突。



这是我使用的类:

  #include< string.h> 
#include< utility>

class String
{
int len;
char * data;

public:
//默认构造函数
//关于C-String构造函数
String()
:String()
{}

//正则构造函数接受一个C字符串
String(char const * cString)
:len(strlen(cString))
, data(new char [len + 1]())//分配和零内存
{
memcpy(data,cString,len);
}

//标准规则三
String(String const& cpy)
:len(cpy.len)
,data [len + 1]())
{
memcpy(data,cpy.data,len);
}
String& operator =(String rhs)
{
rhs.swap(* this);
return * this;
}
〜String()
{
delete [] data;
}
//标准交换以方便规则三
void swap(String& other)throw()
{
std :: swap(len,other。 len);
std :: swap(data,other.data);
}

// New Stuff
//移动运算符
String(String&& rhs)throw()
:len b $ b,data(null)
{
rhs.swap(* this);
}
String& operator =(String&& rhs)throw()
{
rhs.swap(* this);
return * this;
}
};

我认为很漂亮的标准。



然后我测试我的代码像这样:

  int main()
{
String a );
a = String(Test Move Assignment);
}

这里分配给 a 应使用移动分配运算符。但是,与标准作业操作符(被写为标准副本和交换)有冲突。

 > g ++ --version 
配置:--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版本5.0(clang-500.2.79)(基于LLVM 3.3svn)
目标:x86_64-apple-darwin13.0.0
线程模型:posix

> g ++ -std = c ++ 11 String.cpp
String.cpp:64:9:error:使用重载的运算符'='是不明确的(操作数类型为String和String)
a = String(Test Move Assignment);
〜^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
String.cpp:32:17:注意:候选函数
String& operator =(String rhs)
^
String.cpp:54:17:note:candidate function
String& operator =(String&& rhs)
^



现在我可以通过修改Standard Assignment操作符:

  String& operator =(String const& rhs)
{
String copy(rhs);
copy.swap(* this);
return * this;
}

但这不好,因为它会影响编译器优化副本的能力和交换。请参阅什么是复制和交换惯用法? 此处此处



我缺少不那么明显的东西?

解决方案

你定义赋值运算符取一个值,你不应该(不需要和不能)定义赋值运算符取一个右值引用。没有意义。



一般来说,你只需要提供一个重载,当你需要区分一个左值和一个右值,这种情况下,你选择的实现意味着你不需要做出这种区分。无论你有一个左值或右值,你将创建参数并交换内容。

  String f(); 
String a;
a = f(); // with String&在这种情况下,编译器将解析调用<$ c $ <$> $ <$> <$>

c> a.operator =(f());
它会意识到返回值的唯一原因是 operator = 并且将会删除任何副本 - 这是让函数取第一个值的地方!


Testing out the new Move Semantics.

I just asked about an issues I was having with the Move Constructor. But as it turns out in the comments the problem is really that the "Move Assignment" operator and "Standard Assignment" operator clash when you use the standard "Copy and Swap" idiom.

This is the class I am using:

#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;
        }
};

Pretty bog standard I think.

Then I tested my code like this:

int main()
{
    String  a("Hi");
    a   = String("Test Move Assignment");
}

Here the assignment to a should use the "Move Assignment" operator. But there is a clash with the "Standard Assignment" operator (which is written as your standard copy and swap).

> 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)
                ^

Now I can fix this by modifying the "Standard Assignment" operator to:

    String& operator=(String const& rhs)
    {
        String copy(rhs);
        copy.swap(*this);
        return *this;
    }

But this is not good as it messes with the compiler's ability to optimize the copy and swap. See What is the copy-and-swap idiom? here and here

Am I missing something not so obvious?

解决方案

If you define the assignment operator to take a value, you should not (need not and cannot) define the assignment operator taking an rvalue-reference. There is no point to it.

In general, you only need to provide an overload taking an rvalue-reference when you need to differentiate an lvalue from an rvalue, but in this case your choice of implementation means that you don't need to make that distinction. Whether you have an lvalue or an rvalue you are going to create the argument and swap the contents.

String f();
String a;
a = f();   // with String& operator=(String)

In this case, the compiler will resolve the call to be a.operator=(f()); it will realize that the only reason for the return value is being the argument to operator= and will elide any copy --this is the point of making the function take a value in the first place!

这篇关于移动分配与标准复制和交换不兼容的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆