2016-11-02 3 views
2

나는 이동 생성자에 대해 머리를 감싸려고 노력 중이며이 질문을 통해 좀 더 통찰력을 얻기를 바랍니다. 다음은 간단한 클래스입니다.이동 생성자에는 이동 가능한 속성이 필요합니까?

class A 
{ 
private: 
    vector<B> Bs; 
public: 
    /* 
    .. 
    */ 

    A(A&& other) 
    : Bs 
    { 
     Bs = other.Bs; 
    } 
} 

B는 이동 생성자이없는 경우에도 내 이동 생성자가 제대로 표시합니까? B 클래스의 객체에 대한 이동 지정을 명시 적으로 작성하지 않은 경우에도 이동 생성자가 효과적입니까? 그렇지 않다면, 어떤 물건을 옮기고 싶다면 먼저 각 물건의 속성을 움직일 수 있어야한다는 것을 의미합니까?

+0

[this] (http://thbecker.net/articles/rvalue_references/section_05.html)을보십시오. – wally

답변

4

개체에 이동 가능한 개체 (예 : std::vector)가 있으면 기본 이동 생성자가 이동을 처리하므로 아무 것도 할 필요가 없습니다. Rule of Zero을 사용해보십시오.

귀하의 경우, 아니오, 이동 관리자가 올바른 일을하지 않습니다. 그것은 이름을 가지기 때문에

Bs = other.Bs; 

other.Bs는 (예, r- 수치가 기준을 의미하지만 other 자체가 좌변이다) 함수 내부에서 좌변 인 이후에 복사된다. 당신은

Bs = std::move(other.Bs); 

이상

A(A&& other) : Bs(std::move(other.Bs)) {} 

그러나 다시,이 경우 당신은 정말 모든 사용자 정의 이동 생성자를 작성해서는 안 필요합니다.

가 높은 Howard Hinnant 읽기 권장, 아마도 이동 의미의 개념에 가장 기여하는 사람은 : http://www.slideshare.net/ripplelabs/howard-hinnant-accu2014

+0

@LogicStuff 물론 답변을 업데이트했습니다. – vsoftco

+1

@LogicStuff 더 나은'A (A && other) = 기본; ' – NathanOliver

+0

좋아, 그게 도움이. 그러나 클래스 B가 이동 구조를 가지고 있지 않더라도 (B의 속성으로 간주되지 않더라도) 실제로 작동 할 것인가? 'A'의 유일한 속성이 벡터이므로 이동 생성자가 있으면 벡터에 포함 된 내용에 신경 쓸 필요가 없습니다. –

3

아니, 아니다. 여기에 귀하의 라인 :

Bs = other.Bs; 

복사 지정을하고 있습니다. 이동 생성자 본문에 있으면 표현식 값 유형이 변경되지 않습니다. lvalue는 여전히 lvalues이고 rvalue는 여전히 rvalue입니다.

Bs = std::move(other.Bs); 

을하지만 여전히, 모든 경우에 적용 할 수있는 효율적이지 않다 :

가 이동 할당을 수행하기 위해서는 다음과 같을 것이다.

다른 문제가 있습니다. std::vector 생성자에 중괄호를 위탁했기 때문에 코드가 컴파일되지 않습니다. 실제로이 값을 이동해야합니다.

// a kitten dies when your move constructor is not noexcept 
A(A&& other) noexcept 
    // in the move constructor of A, we move construct it's member too. 
: Bs{std::move(other.Bs)} 
// empty body 
{} 

모두의 가장 좋은 해결책은 이것이다 :

자네 말이 맞아, 그것은 비어 다음은 예입니다. 생성자를 넣지 않으면 컴파일러가 대신 해줍니다. 다른 생성자를 추가하지만 컴파일러가 자신의 이동 생성자를 추가 할 수 있도록하려면

, 당신은 명시 적으로 기본 수 있습니다

// bonus: noexcept when it can. 
A(A&&) = default; 

마지막 일을.클래스에 이동 가능하지만 복사 불가능한 다른 클래스가 포함되어있는 경우, 클래스는 가능한 한 할 것입니다 : 복사 생성.

유형 C은 움직일 수없는 클래스지만 복사 할 수 있다고합시다.

다음은 예입니다 :

// the `C&&` from the move will bind to the `const C&` of the operator= 
auto anotherC = std::move(aC); 
+0

매우 도움이되는 +1입니다. (1) 명시 적으로 이동 생성자를 쓰고 싶지 않은 클래스의 경우, A (A &&) = default;를 추가하지 않을 이유가 없습니까? (2) 내가 올바르게 이해한다면, 당신이 답안에 쓴 '수작업'이동 생성자보다 정확하게 (A (A &&) = default;)가 똑같을 것이라고 기대할 것입니다. –

+1

@Remi .b 기본 이동 생성자에 대한 좋은 점은 장래에 다른 이동 가능 멤버를 클래스에 추가하기로 결정한 경우 이동 관리자를 계속 수정할 필요가 없다는 것입니다. – vsoftco

+0

복사 생성자를 정의하면 컴파일러가 자신의 이동 생성자를 구현하지 못합니다. 이 경우'A (A &&) = default; '가 매우 유용합니다. 그렇지 않으면 컴파일러에서 기본적으로 기본값을 추가합니다. –

1

여기에 이동 생성자는 A 객체 유형 A의를 rvalue로 구성 될 때 호출 할 것이다에 "효과"가됩니다. 그것은 다른 개체의 유효한 복사본을 만드는 잘 작동합니다. 그러나 내부 std::vector 개체를 복사하기 때문에 가능한 한 효과적이지 않습니다. 이는 성능 문제이며 정확성 문제는 아닙니다.

여기에는 실제로 두 가지 성능 문제가 있습니다. 첫 번째는 생성자 default가 B 하위 객체를 생성 한 다음 해당 객체에 할당한다는 것입니다. 그건 낭비입니다. 이 문제를 해결하려면

A::A(A&& other) : B(other.B) { 
} 

두 번째는 이동 생성자가 다른 개체에서 도용 할 수 있다는 것입니다. 이를 위해 구현이 B 하위 객체를 이동해야합니다

마지막으로
A::A(A&& other) : B(std::move(other.B)) { 
} 

는 제목의 질문에 대답하기 위해, std::move 이동하지 않은 유형의 잘 작동합니다. 그것은 lvalue를 rvalue로 변환하는 것뿐입니다. non-movable 타입은 보통 rvalues에서 복사 할 수 있습니다. 따라서 매우 비뚤어진 타입을 다루지 않는 한, 객체의 거의 모든 것에 std::move을 사용하면됩니다. (int 값을 이동하는 것이 이상하게 보일 수도 있지만 무해합니다.)