문제

새로운 움직임 시맨틱을 테스트합니다.

방금 Move 생성자와 관련된 문제에 대해 물었습니다. 그러나 의견에서 밝혀진 바와 같이 문제는 실제로 표준 "사본 및 스왑"관용구를 사용할 때 "이동 할당"연산자와 "표준 할당"연산자 충돌이 실제로 있다는 것입니다.

이것은 내가 사용하는 수업입니다.

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

그러나 이것은 컴파일러가 사본과 스왑을 최적화하는 능력을 망쳐 놓기 때문에 좋지 않습니다. 카피 앤 스왑 관용구는 무엇입니까? 여기 그리고 여기

그렇게 분명하지 않은 것을 놓치고 있습니까?

도움이 되었습니까?

해결책

할당 연산자를 값을 취하도록 정의하는 경우 RValue 참조를 취하는 할당 연산자를 정의하지 않아야합니다. 그것에 대한 점이 없습니다.

일반적으로 rvalue와 lvalue를 차별화해야 할 때 rvalue reference를 취하는 과부하 만 제공하면되지만이 경우 구현을 선택하면 해당 구별 할 필요가 없습니다. lvalue 또는 rvalue가 있든 논쟁을 만들고 내용을 교환 할 것입니다.

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

이 경우 컴파일러는 호출을 해결합니다. a.operator=(f()); 반환 값의 유일한 이유는 operator= 그리고 모든 사본을 피할 것입니다 -이것은 기능이 처음에 값을 가져 오는 지점입니다!

다른 팁

다른 답변은 하나의 과부하 만있는 것을 제안합니다 operator =(String rhs) 가치로 논쟁을 취하지 만 이것은입니다 ~ 아니다 가장 효율적인 구현.

이 예에서 David Rodríguez -Dribeas가

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

사본이 없습니다. 그러나 그냥 가정하십시오 operator =(String rhs) 제공 되고이 예를 고려합니다.

String a("Hello"), b("World");
a = b;

무슨 일이 일어나는지

  1. b 복사됩니다 rhs (메모리 할당 + memcpy);
  2. a 그리고 rhs 교환됩니다.
  3. rhs 파괴됩니다.

우리가 구현하는 경우 operator =(const String& rhs) 그리고 operator =(String&& rhs) 그런 다음 대상의 길이가 소스보다 큰 경우 1 단계에서 메모리 할당을 피할 수 있습니다. 예를 들어, 이것은 간단한 구현입니다 (완벽하지 않음 : 더 나을 수 있습니다. String a 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
   }
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top