2016-06-07 1 views
-1

가상 메소드가있는 클래스 기반과 가상 메소드를 구현하는 구현 클래스가있는 경우 std :: shared_ptr을 캐스팅 할 방법이 있습니까 < 구현> &을 std :: shared <베이스> &으로 변환 하시겠습니까? 컴파일러는 const 참조를 허용하지만 비 const 참조의 경우 아래 코드의 "사례 A"에서처럼 실패합니다. 이 작업을 수행하는 쉬운 방법이 있습니까?shared_ptr의 non-const 참조를 기본 클래스로 캐스트하는 더 안전하고 안전한 방법이 있습니까?

그렇지 않은 경우 케이스 B에서 해결 방법 "questionable_cast"를 사용해도 안전합니까? 이 문제에

#include <iostream> 
#include <memory> 

class Base 
{ 
public: 
    virtual void set_value(int x) = 0; 
}; 

class Implementation : public Base 
{ 
public: 
    Implementation() : m_value(0) { } 
    void set_value(int x) override 
    { 
    m_value = x; 
    } 
    int get_value() const 
    { 
    return m_value; 
    } 
private: 
    int m_value; 
}; 


void do_something(std::shared_ptr<Base>& base) 
{ 
    base->set_value(5); 

    /// Code like this makes the non-const argument necessary 
    base = std::make_shared<Implementation>(); 
} 

template <class T, class U> 
std::shared_ptr<T>& questionable_cast(std::shared_ptr<U>& u) 
{ 
    /// This code is here to assure the cast is allowed 
    std::shared_ptr<T> tmp = u; 
    (void)tmp; 

    return *reinterpret_cast<std::shared_ptr<T>*>(&u); 
} 

int main() 
{ 
    std::shared_ptr<Implementation> a = std::make_shared<Implementation>(); 

    // The following line causes a compiler error: 
    // invalid initialization of reference of type ‘std::shared_ptr<Base>&’ ... 
    // do_something(a); 
    // do_something(std::dynamic_pointer_cast<Base>(a)); 

    // This is the workaround 
    do_something(questionable_cast<Base>(a)); 

    std::cerr << "a = " << a->get_value() << std::endl; 

    return 0; 
} 
+0

보너스 질문 : 왜이 ​​기능이 다운 되었습니까? – bofjas

+0

왜'questionable_cast' 함수에서와 같이 복사본을 만드는 대신 레퍼런스를 사용하려고합니까? 참조를 사용하면 카운터가 증가하지 않으므로 참조가 무효화 될 수 있습니다. –

+2

나는 downvote하지 않았지만 가능한 이유 : * 왜 * 당신이 이것을하고 싶어합니까? 이와 같이 reinterpret_cast를 사용하면 엄격한 앨리어싱 규칙을 위반하게되므로 정의되지 않은 동작이됩니다. –

답변

2

두 명백한 솔루션은 원래 질문으로 : 1. 만들기는 do_something shared_ptr을 (또는 값에 의해 shared_ptr을)에 const를 참조 가져 가라. 2. 이름이있는 shared_ptr을 작성하고 그에 대한 참조를 전달 : 예

int main() 
{ 
    std::shared_ptr<Implementation> a = std::make_shared<Implementation>(); 
    std::shared_ptr<Base> b = a; // This conversion works. 
    do_something(b); // Pass a reference to b instead. 
    return 0; 
} 

당신의 questionable_cast 기능은 엄격한 앨리어싱 규칙의 위반이며, 정의되지 않은 동작을 호출합니다. 그것은 초기 테스트에서 작동 할 가능성이 매우 높습니다. 그리고 컴파일러의 새로운 릴리즈는 최적화를 한 단계 끌어 올릴 것이며, 데모 중에는 실패 할 것입니다.

do_something 포인터 변경하는 경우 처리하려면 :

int main() 
{ 
    std::shared_ptr<Implementation> a = std::make_shared<Implementation>(); 
    std::shared_ptr<Base> b = a; // This conversion works. 
    do_something(b); // Pass a reference to b instead. 
    const auto aa = std::dynamic_pointer_cast<Implementation>(b); 
    if (aa) 
     a = aa; 
    else 
     ; // Handle the error here 
    return 0; 
} 

do_something 만약 보장이 같은 포인터를 반환하지 않는 경우에도, 같은 파생 타입의 포인터를 반환 템플릿에 포장하기를 함수 :

template <typename T> 
void do_something_ex(std::shared_ptr<T>& a) 
{ 
    std::shared_ptr<Base> b = a; 
    do_something(b) 
    a = std::dynamic_pointer_cast<T>(b); 
    if (!a) 
     throw_or_assert; 
} 
+0

확인. 죄송합니다. do_something의 중요한 기능을 잊어 버렸습니다. 또한 shared_ptr을 변경할 수도 있습니다. 그러니 당신의 제안은 효과가 없을 것입니다. – bofjas

+0

@bofjas : 여전히 가능하며, 그러한 변경 사항은 'b'에 반영됩니다. 그것들을'a'에 반영하려면'b'를 다시 복사하십시오. –

+0

그래, 내가 그 일을하고 싶지 않은 이유는 dynamic_pointer_cast를 수행하고 결과를 확인해야하기 때문에 내가 함수라고 부르는 모든 장소에서 많은 코드가있을 것이기 때문이다. 내 접근 방식으로 무엇이 안전하지 않은지 말해주었습니다. 내가 그 해결책에 붙어있는 것 같아요. 그래도 도와 ​​줘서 고마워! – bofjas

관련 문제