2017-11-25 14 views
2

아래의 복사 생성자가 제대로 작동하지만 복사 할당 연산자가 잘못되었음을 이해하지 못합니다.다중 상속을 사용하는 복사 할당 연산자

#include <iostream> 

template <typename... Ts> class foo; 

template <typename Last> 
class foo<Last> { 
    Last last; 
public: 
    foo (Last r) : last(r) { } 
    foo() = default; 
    foo (const foo& other) : last(other.last) { } 

    foo& operator= (const foo& other) { 
     last = other.last; 
     return *this; 
    } 
}; 

template <typename First, typename... Rest> 
class foo<First, Rest...> : public foo<Rest...> { 
    First first; 
public: 
    foo (First f, Rest... rest) : foo<Rest...>(rest...), first(f) { } 
    foo() = default; 
    foo (const foo& other) : foo<Rest...>(other), first(other.first) { std::cout << "[Copy constructor called]\n"; } 

    foo& operator= (const foo& other) { // Copy assignment operator 
     if (&other == this) 
      return *this; 
     first = other.first; 
     return foo<Rest...>::operator= (other); 
    } 
}; 

int main() { 
    const foo<int, char, bool> a(4, 'c', true); 
    foo<int, char, bool> b = a; // Copy constructor works fine. 
    foo<int, char, bool> c; 
// c = a; // Won't compile. 
} 

오류 메시지 :

error: invalid initialization of reference of type 'foo<int, char, bool>&' from expression of type 'foo<char, bool>' 
     return foo<Rest...>::operator= (other); 
              ^

누군가가 여기에 문제를 지적 할 수 있습니까?

+0

왜 복사 및 스왑을 사용하지 않습니까? 스왑 가능하지 않거나 측정 가능한 성능 문제가 아닌'T '를 기대합니까? –

답변

7

귀하의 return 문

return foo<Rest...>::operator= (other); 

돌려 foo<Rest...> (즉으로 정의 참조 operator=의 유형이다). 그러나 연산자를 사용하면 foo<First, Rest...>&을 돌려 주어야합니다.

기본적으로 Derived& 참조가 예상되는 Base을 반환합니다. 이 참조는 단순히 묶이지 않습니다.

Fortunatly 수정 방법은 간단합니다. foo<Rest...>::operator=의 결과를 반환하지 말고 대신 *this을 반환하십시오.

foo& operator= (const foo& other) { // Copy assignment operator 
    if (&other == this) 
     return *this; 
    first = other.first; 
    foo<Rest...>::operator= (other); 
    return *this; 
} 
+0

@ Story Teller 네, 사실입니다. 보너스로'if (& other == this) return * this;'가 여러 번 호출되었지만 중복 되더라도 (한 번만 호출하면됩니다)? 하지만 이것을 피할 방법이 없을까요? – prestokeys

+0

@prestokeys - 체크하지 않은 (재귀 적으로 구현 된) 다른 명명 된 멤버에 항상'operator ='델리게이트 할 수 있습니다. – StoryTeller

+0

@ StoryTeller 답변을 수락하고 별도의 답변으로 최적화하기 위해 귀하의 아이디어를 구현했습니다. 감사. – prestokeys

1

는 파생 클래스에서 operator=에서 반환처럼 보이는 것은 올바르지 않습니다 : 그것은 기본 클래스를 반환

return foo<Rest...>::operator= (other); 

, 그것은 *this을해야합니다 동안. 이야기꾼에

this -> foo<Rest...>::operator= (other); 
return *this; 
0

감사로 변경, 여기에 완전히 컴파일 최적화 된 솔루션입니다 (연산자 = 자기 할당을 확인하지 않는 다른 이름 멤버 이름 copy_data에 위임, 재귀 적 구현) :

#include <iostream> 

template <typename... Ts> class foo; 

template <typename Last> 
class foo<Last> { 
    Last last; 
public: 
    foo (Last r) : last(r) { } 
    foo() = default; 
    foo (const foo& other) : last(other.last) { } 

    foo& operator= (const foo& other) { 
     if (&other == this) 
      return *this; 
     last = other.last; 
     return *this; 
    } 
protected: 
    void copy_data (const foo& other) { 
     last = other.last; 
    } 
}; 

template <typename First, typename... Rest> 
class foo<First, Rest...> : public foo<Rest...> { 
    First first; 
public: 
    foo (First f, Rest... rest) : foo<Rest...>(rest...), first(f) { } 
    foo() = default; 
    foo (const foo& other) : foo<Rest...>(other), first(other.first) { std::cout << "[Copy constructor called]\n"; } 

    foo& operator= (const foo& other) { // Copy assignment operator 
     if (&other == this) 
      return *this; 
     first = other.first; 
//  foo<Rest...>::operator= (other); 
     foo<Rest...>::copy_data(other); 
     std::cout << "[Assignment operator called]\n"; 
     return *this; 
    } 
protected: 
    void copy_data (const foo& other) { 
     first = other.first; 
     foo<Rest...>::copy_data(other); 
    } 
}; 

int main() { 
    const foo<int, char, bool> a(4, 'c', true); 
    foo<int, char, bool> b = a; // Copy constructor works fine. 
    foo<int, char, bool> c; 
    c = b; // Copy assignment operator works fine (and optimized). 
}