C++ 移动赋值运算符和“如果”(此!=&;rhs)`

C++ 移动赋值运算符和“如果”(此!=&;rhs)`,c++,c++11,move-semantics,move-assignment-operator,C++,C++11,Move Semantics,Move Assignment Operator,在类的赋值操作符中,通常需要检查被赋值的对象是否是调用对象,这样就不会把事情搞砸: Class& Class::operator=(const Class& rhs) { if (this != &rhs) { // do the assignment } return *this; } 移动分配操作符是否需要相同的内容?是否曾经出现过这样的情况:this==&rhs ? Class::operator=(Class&&

在类的赋值操作符中,通常需要检查被赋值的对象是否是调用对象,这样就不会把事情搞砸:

Class& Class::operator=(const Class& rhs) {
    if (this != &rhs) {
        // do the assignment
    }

    return *this;
}
移动分配操作符是否需要相同的内容?是否曾经出现过这样的情况:
this==&rhs

? Class::operator=(Class&& rhs) {
    ?
}

在编写当前的
运算符=
函数时,由于您已经创建了右值引用参数
const
,因此无法“窃取”指针并更改传入右值引用的值。。。你根本无法改变它,你只能从中阅读。我只会看到一个问题,如果你开始调用
delete
指针等。在
这个
对象中,就像在普通的lvaue引用
operator=
方法中一样,但是这种方法会破坏右值版本的观点。。。i、 例如,使用右值版本来执行通常留给
const
-lvalue
操作符=
方法的相同操作似乎是多余的

现在,如果您将
运算符=
定义为接受一个非
常量
右值引用,那么我看到需要进行检查的唯一方法是将
对象传递给一个有意返回右值引用而不是临时值的函数

例如,假设有人试图编写一个
运算符+
函数,并利用右值引用和左值引用的混合,以“防止”在对对象类型执行某些堆叠加法操作期间创建额外的临时值:

struct A; //defines operator=(A&& rhs) where it will "steal" the pointers
          //of rhs and set the original pointers of rhs to NULL

A&& operator+(A& rhs, A&& lhs)
{
    //...code

    return std::move(rhs);
}

A&& operator+(A&& rhs, A&&lhs)
{
    //...code

    return std::move(rhs);
}

int main()
{
    A a;

    a = (a + A()) + A(); //calls operator=(A&&) with reference bound to a

    //...rest of code
}

现在,根据我对右值引用的理解,不鼓励执行上述操作(即,您应该只返回一个临时的,而不是右值引用),但是,如果有人仍然这样做,然后,您需要检查以确保传入的右值引用没有引用与
指针相同的对象。

在编写当前的
运算符=
函数时,由于您已经创建了右值引用参数
const
,因此无法“窃取”指针并更改传入右值引用的值。。。你根本无法改变它,你只能从中阅读。我只会看到一个问题,如果你开始调用
delete
指针等。在
这个
对象中,就像在普通的lvaue引用
operator=
方法中一样,但是这种方法会破坏右值版本的观点。。。i、 例如,使用右值版本来执行通常留给
const
-lvalue
操作符=
方法的相同操作似乎是多余的

现在,如果您将
运算符=
定义为接受一个非
常量
右值引用,那么我看到需要进行检查的唯一方法是将
对象传递给一个有意返回右值引用而不是临时值的函数

例如,假设有人试图编写一个
运算符+
函数,并利用右值引用和左值引用的混合,以“防止”在对对象类型执行某些堆叠加法操作期间创建额外的临时值:

struct A; //defines operator=(A&& rhs) where it will "steal" the pointers
          //of rhs and set the original pointers of rhs to NULL

A&& operator+(A& rhs, A&& lhs)
{
    //...code

    return std::move(rhs);
}

A&& operator+(A&& rhs, A&&lhs)
{
    //...code

    return std::move(rhs);
}

int main()
{
    A a;

    a = (a + A()) + A(); //calls operator=(A&&) with reference bound to a

    //...rest of code
}

现在,根据我对右值引用的理解,不鼓励执行上述操作(即,您应该只返回一个临时的,而不是右值引用),但是,如果有人仍然这样做,那么您需要检查以确保传入的右值引用没有引用与
指针相同的对象。

哇,这里有太多的东西要清理

首先,复制并不总是实现复制分配的正确方法。几乎可以肯定的是,在
哑_阵列
的情况下,这是一个次优解决方案

is for
dumb_array
是将最昂贵的操作和最完整的功能放在底层的经典示例。它是完美的客户谁想要最完整的功能,并愿意支付性能罚款。他们得到的正是他们想要的

但是,对于那些不需要最全面的功能,而是寻求最高性能的客户来说,这是灾难性的。对他们来说,dumb_array
只是另一个他们必须重写的软件,因为它太慢了。如果
dumb_array
的设计不同,它可以满足两个客户的需求,而不会对任何一个客户造成任何损害

满足这两个客户机的关键是在最低级别上构建最快的操作,然后在此基础上添加API,以更高的成本实现更全面的功能。也就是说,你需要强有力的例外担保,好吧,你自己付钱。你不需要它?这里有一个更快的解决方案

下面是
dumb_数组
的快速基本异常保证拷贝分配操作符:

dumb_array& operator=(const dumb_array& other)
{
    if (this != &other)
    {
        if (mSize != other.mSize)
        {
            delete [] mArray;
            mArray = nullptr;
            mArray = other.mSize ? new int[other.mSize] : nullptr;
            mSize = other.mSize;
        }
        std::copy(other.mArray, other.mArray + mSize, mArray);
    }
    return *this;
}
dumb_array& operator=(dumb_array&& other)
{
    assert(this != &other);
    delete [] mArray;
    mSize = other.mSize;
    mArray = other.mArray;
    other.mSize = 0;
    other.mArray = nullptr;
    return *this;
}
说明:

在现代硬件上,你可以做的一件更昂贵的事情就是到堆里去一趟。你能做的任何事情都是花时间和精力来避免陷入困境的。
dumb_array
的客户端可能希望经常分配相同大小的数组。当他们这样做时,您只需要做一个
memcpy
(隐藏在
std::copy
下)。您不希望分配相同大小的新数组,然后取消分配相同大小的旧数组

现在,对于真正需要强异常安全性的客户:

template <class C>
C&
strong_assign(C& lhs, C rhs)
{
    swap(lhs, rhs);
    return lhs;
}
这其实是一个有争议的问题。有些人会说是的,绝对的,有些人会说不

我个人的意见是不,你不需要这张支票

理由:

当对象绑定到右值引用时,它是以下两种情况之一:

  • 临时的
  • 来电者希望您相信的对象是临时对象
  • 如果对某个对象的引用是实际的临时对象,那么根据定义,对该对象的引用是唯一的。它不可能被整个p中的任何其他地方引用
    Class&
    Class::operator=(Class&& other)
    {
        assert(this != &other);
        // ...
        return *this;
    }
    
    dumb_array& operator=(dumb_array&& other)
    {
        assert(this != &other);
        delete [] mArray;
        mSize = other.mSize;
        mArray = other.mArray;
        other.mSize = 0;
        other.mArray = nullptr;
        return *this;
    }
    
    dumb_array& operator=(dumb_array&& other)
    {
        assert(this != &other || mSize == 0);
        delete [] mArray;
        mSize = other.mSize;
        mArray = other.mArray;
        other.mSize = 0;
        other.mArray = nullptr;
        return *this;
    }
    
    x = y;
    
    x = std::move(y);
    
    template <class T>
    void
    swap(T& x, T& y)
    {
        // assume &x == &y
        T tmp(std::move(x));
        // x and y now have a valid but unspecified state
        x = std::move(y);
        // x and y still have a valid but unspecified state
        y = std::move(tmp);
        // x and y have the value of tmp, which is the value they had on entry
    }
    
    dumb_array& operator=(dumb_array&& other)
    {
        delete [] mArray;
        // set *this to a valid state before continuing
        mSize = 0;
        mArray = nullptr;
        // *this is now in a valid state, continue with move assignment
        mSize = other.mSize;
        mArray = other.mArray;
        other.mSize = 0;
        other.mArray = nullptr;
        return *this;
    }
    
    dumb_array& operator=(dumb_array&& other)
    {
        if (this != &other)
        {
            delete [] mArray;
            mSize = other.mSize;
            mArray = other.mArray;
            other.mSize = 0;
            other.mArray = nullptr;
        }
        return *this;
    }
    
    dumb_array& operator=(dumb_array&& other)
    {
        swap(other);
        return *this;
    }
    
    Class& operator=(Class&&) = default;
    
    Expression Return type Return value Post-condition t = rv T& t t is equivalent to the value of rv before the assignment
    Class &Class::operator=( Class &&rhs ) {
        //...
        return *this;
    }
    
    dumb_array& dumb_array::operator=(const dumb_array& other)
    {
        if (mSize != other.mSize)
        {
            delete [] mArray;
            mArray = nullptr;  // clear this...
            mSize = 0u;        // ...and this in case the next line throws
            mArray = other.mSize ? new int[other.mSize] : nullptr;
            mSize = other.mSize;
        }
        std::copy(other.mArray, other.mArray + mSize, mArray);
        return *this;
    }
    
    class dumb_array
    {
        //...
        void swap(dumb_array& other) noexcept
        {
            // Just in case we add UDT members later
            using std::swap;
    
            // both members are built-in types -> never throw
            swap( this->mArray, other.mArray );
            swap( this->mSize, other.mSize );
        }
    
        dumb_array& operator=(dumb_array&& other) noexcept
        {
            this->swap( other );
            return *this;
        }
        //...
    };
    
    void  swap( dumb_array &l, dumb_array &r ) noexcept  { l.swap( r ); }
    
    class dumb_array
    {
        //...
        dumb_array& operator=(dumb_array&& other) noexcept
        {
            delete [] this->mArray;  // kill old resources
            this->mArray = other.mArray;
            this->mSize = other.mSize;
            other.mArray = nullptr;  // reset source
            other.mSize = 0u;
            return *this;
        }
        //...
    };
    
    class dumb_array
    {
        //...
        dumb_array& operator=(dumb_array&& other) noexcept
        {
            dumb_array  temp{ std::move(other) };
    
            this->swap( temp );
            return *this;
        }
        //...
    };
    
    unique_ptr& operator=(unique_ptr&& x) {
      delete ptr_;
      ptr_ = x.ptr_;
      x.ptr_ = nullptr;
      return *this;
    }
    
    src.erase(
      std::partition_copy(src.begin(), src.end(),
                          src.begin(),
                          std::back_inserter(even),
                          [](int num) { return num % 2; }
                          ).first,
      src.end());