2014-07-24 2 views
0

복사 생성자가 있다고 가정합니다. 이 생성자는 복사 된 객체를 lvalue 참조로 전달하여 함수 계층을 호출합니다.lvalues ​​및 rvalues에 대한 코드 재사용

이제 기본적으로 복사 생성자와 동일한 함수 계층 구조를 사용할 수있는 이동 생성자가 있습니다. rvalue 인수를 lvalue 계층 구조에 전달할 수 있으므로이 작업을 수행 할 수 있습니다.

그러나 어딘가에 계층 구조에서 나는 lvalue 경우에 리소스를 복사하고 rvalue 경우에 리소스를 '훔칠'기능이 있습니다.

해당 함수에 전달 된 lvalue 참조가 rvalue에서 유래했는지 여부를 결정하는 방법이 있습니까? 나는 그렇지 않다. 또는 복사 및 이동 구조에 사용할 수 있고 거의 기능이 거의 다른 복사 기능 계층 구조가있는 경우 일반적인 방법은 무엇입니까?

코드 샘플 :

class A{ 
    A(const A& a){ 
     initFrom(a); 
    } 

    A(A&& a){ 
     initFrom(a); 
    } 

    void initFrom(const A& a){ 
     // call a hierarchy of functions, of which one of them calls initResource(const A&) 
    } 

    void initResource(const A& a){ 

     if(a == rvalue reference){ // **** Here's the question... **** 
      // steal resource 
      this->ptr = a.ptr; 
      a.ptr = nullptr; 
     } 
     else{ 
      // copy resource 
      this->ptr = allocate... 
      copy from a.ptr to this->ptr 
     } 

    } 
+2

우리에게 보여줄 수있는 예가 있습니까? –

+0

'enable_if'와'is_rvalue_reference'가 도움이 될듯한 느낌이 들지만, 코드 샘플 (MCVE)은 더 많은 문맥을 제공합니다. – Niall

+0

누군가가 질문을 향상시킬 것을 요구할 때, 질문의 끝 부분에 물건을 넣고 "요청시 X 추가"라고 말하지 마십시오. 오히려, 전체적으로 더 나을 수 있도록 * 귀하의 질문을 재 작업하십시오. –

답변

2

이 완벽 전달의 전형적인 예입니다

template <typename T> 
A(T && t) { initFrom(std::forward<T>(a)); } 

template <typename T> 
void initFrom(T && t) 
{ 
    // other calls 
    initResource(std::forward<T>(t)); 
} 

void initResource(A const & rhs) { /* copy from rhs */ } 
void initResource(A && rhs)  { /* move from rhs */ } 

(당신이 중 하나를 다른 클래스를 생성자에 initFrom 병합, 또는 할 수 있어야한다 보인다 너무 많은 노력을하고있을 수 있으며 단일 책임 구성 요소로 리팩터링해야합니다.)

+0

좋아, 나는 "완벽한 포워딩"이이 질문에 대한 완벽한 대답이라고 말할 것입니다. 감사합니다! (그러나 리팩토링에 대한 힌트를 간단한 구성 요소로 생각할 것입니다.) – Michael

1

여기서 한 가지 대안은 initFrom을 사용하여 참조 축소를 허용하는 "범용 참조"를 허용 한 다음 완벽한 전달을 위해 std::forward을 사용하십시오. 나머지 호출 계층 구조를 다시 고려해야 할 수도 있습니다.

class A{ 
    A(const A& a){ 
     initFrom(a); 
    } 

    A(A&& a){ 
     initFrom(a); 
    } 

    template <typename B> 
    void initFrom(B&& a){ // reference collapsing applies 
     // call a hierarchy of functions, of which one of them calls initResource(const A&) 
     initResource(std::forward<B>(a)); 
    } 

    void initResource(A&& a){ 
     // steal resource 
     this->ptr = a.ptr; 
     a.ptr = nullptr; 
    } 

    void initResource(const A& a){ 
     // copy resource 
     this->ptr = allocate... 
     //copy from a.ptr to this->ptr 
    } 
}; 

나는 간단한 대안는 클래스에 최초의 "이동"자원을 initFrom가 호출되기 전에에 생각합니다.

A(A&& a){ 
     this->ptr = a.ptr; 
     a.ptr = nullptr; 
     initFrom(a); 
    } 

여기의 마일리지는 다를 수 있습니다.

1

호출 계층 구조에 따라 오브젝트를 전달할 때를 제외하고는 모든 기능이 수행해야하는 작업은 어쨌든 오브젝트를 클래스 내에 저장하려는 경우 다른 기술을 사용할 수 있습니다.

class A { 
    A(const A& a) { 
     initFrom(A(a)); // take a copy here 
    } 
    A(A&& a) { 
     initFrom(std::move(a)); // move here 
    } 

    void initFrom(A&& a) { 
     initResource(std::move(a)); // just pass down 
    } 

    void initResource(A&& a) { 
     // you already have your copy of a here that you can store completely 
     // or take its guts 
    } 

만 (AN를 rvalue 참조 용) 및 이동 또는 메서드 호출 내에서 직접 처리됩니다 사본을 가지고 있는지 한 번 모든 메소드를 구현해야 이쪽으로. rvalue 참조를 전달하려면 항상 std :: move()를 수행해야합니다.