스마트 포인터와 함께 공분산 반환 유형을 어떻게 사용할 수 있습니까?

StackOverflow https://stackoverflow.com/questions/196733

  •  10-07-2019
  •  | 
  •  

문제

다음과 같은 코드가 있습니다.

class RetInterface {...}

class Ret1: public RetInterface {...}

class AInterface
{
  public:
     virtual boost::shared_ptr<RetInterface> get_r() const = 0;
     ...
};

class A1: public AInterface
{
  public:
     boost::shared_ptr<Ret1> get_r() const {...}
     ...
};

이 코드는 컴파일되지 않습니다.

Visual Studio에서는 제기됩니다

C2555 : 가상 기능 재정의 유형은 다르고 공분산이 아닙니다.

내가 사용하지 않는 경우 boost::shared_ptr 그러나 원시 포인터를 반환하면 코드가 컴파일됩니다 (이는 이것이 공분산 반환 유형 C ++). 문제가 있기 때문입니다 boost::shared_ptrRet1 파생되지 않습니다 boost::shared_ptrRetInterface. 그러나 나는 돌아오고 싶다 boost::shared_ptrRet1 다른 클래스에서 사용하려면 반품 후 반환 된 값을 시전해야합니다.

  1. 내가 뭔가 잘못하고 있습니까?
  2. 그렇지 않다면 왜 이런 언어가 이와 같은 언어 인 경우 -이 시나리오에서 스마트 포인터 간의 전환을 처리하는 것이 확장 가능해야합니까? 바람직한 해결 방법이 있습니까?
도움이 되었습니까?

해결책

첫째, 이것은 실제로 C ++에서 작동하는 방식입니다. 파생 클래스에서 가상 함수의 리턴 유형은 기본 클래스와 동일해야합니다. 일부 클래스 X에 대한 참조/포인터를 반환하는 함수가 X에서 파생 된 클래스에 대한 참조/포인터를 반환하는 함수에 의해 재정의 될 수 있다는 특별한 예외가 있지만,이를 통해 허용되지 않습니다. 똑똑한 포인터 (예 : shared_ptr), 단지 평범한 포인터를 위해.

인터페이스 인 경우 RetInterface 충분히 포괄적 인 경우 호출 코드에서 실제 반환 된 유형을 알 필요가 없습니다. 일반적으로 어쨌든 의미가 없습니다 : 이유 get_r a virtual 첫 번째 기능은 포인터 또는 기본 클래스에 대한 참조를 통해 호출하기 때문입니다. AInterface,이 경우 파생 클래스가 어떤 유형이 돌아올 지 알 수 없습니다. 당신이 이것을 실제로 부르는 경우 A1 참조, 당신은 별도를 만들 수 있습니다 get_r1 기능 A1 그것은 당신이 필요로합니다.

class A1: public AInterface
{
  public:
     boost::shared_ptr<RetInterface> get_r() const
     {
         return get_r1();
     }
     boost::shared_ptr<Ret1> get_r1() const {...}
     ...
};

또는 방문자 패턴이나 내와 같은 것을 사용할 수 있습니다. 동적 이중 디스패치 콜백을 반환 된 객체로 전달하는 기술을 통해 올바른 유형으로 콜백을 호출 할 수 있습니다.

다른 팁

이 솔루션은 어떻습니까 :

template<typename Derived, typename Base>
class SharedCovariant : public shared_ptr<Base>
{
public:

typedef Base BaseOf;

SharedCovariant(shared_ptr<Base> & container) :
    shared_ptr<Base>(container)
{
}

shared_ptr<Derived> operator ->()
{
    return boost::dynamic_pointer_cast<Derived>(*this);
}
};

예 :

struct A {};

struct B : A {};

struct Test
{
    shared_ptr<A> get() {return a_; }

    shared_ptr<A> a_;
};

typedef SharedCovariant<B,A> SharedBFromA;

struct TestDerived : Test
{
    SharedBFromA get() { return a_; }
};

C ++에서 오버로드 메소드를 할 때 반환 유형 (비 점수, 비 참조 반환 유형)을 변경할 수 없습니다. A1::get_r 반환해야합니다 boost::shared_ptr<RetInterface>.

Anthony Williams는 좋은 포괄적입니다 대답.

내 시도는 다음과 같습니다.

template<class T>
class Child : public T
{
public:
    typedef T Parent;
};

template<typename _T>
class has_parent
{
private:
    typedef char                        One;
    typedef struct { char array[2]; }   Two;

    template<typename _C>
    static One test(typename _C::Parent *);
    template<typename _C>
    static Two test(...);

public:
    enum { value = (sizeof(test<_T>(nullptr)) == sizeof(One)) };
};

class A
{
public :
   virtual void print() = 0;
};

class B : public Child<A>
{
public:
   void print() override
   {
       printf("toto \n");
   }
};

template<class T, bool hasParent = has_parent<T>::value>
class ICovariantSharedPtr;

template<class T>
class ICovariantSharedPtr<T, true> : public ICovariantSharedPtr<typename T::Parent>
{
public:
   T * get() override = 0;
};

template<class T>
class ICovariantSharedPtr<T, false>
{
public:
    virtual T * get() = 0;
};

template<class T>
class CovariantSharedPtr : public ICovariantSharedPtr<T>
{
public:
    CovariantSharedPtr(){}

    CovariantSharedPtr(std::shared_ptr<T> a_ptr) : m_ptr(std::move(a_ptr)){}

    T * get() final
   {
        return m_ptr.get();
   }
private:
    std::shared_ptr<T> m_ptr;
};

그리고 작은 예 :

class UseA
{
public:
    virtual ICovariantSharedPtr<A> & GetPtr() = 0;
};

class UseB : public UseA
{
public:
    CovariantSharedPtr<B> & GetPtr() final
    {
        return m_ptrB;
    }
private:
    CovariantSharedPtr<B> m_ptrB = std::make_shared<B>();
};

int _tmain(int argc, _TCHAR* argv[])
{
    UseB b;
    UseA & a = b;
    a.GetPtr().get()->print();
}

설명 :

이 솔루션은 메타 프로그램을 암시하고 공분산 스마트 포인터에 사용되는 클래스를 수정합니다.

간단한 템플릿 구조 Child 유형을 바인딩하기 위해 여기에 있습니다 Parent 그리고 상속. 모든 수업에서 상속 Child<T> 상속 될 것입니다 T 그리고 정의 T ~처럼 Parent. 공분산 스마트 포인터에 사용 된 클래스는이 유형을 정의해야합니다.

클래스 has_parent 클래스가 유형을 정의하면 컴파일 시간에 감지하는 데 사용됩니다. Parent 아니면 아니에요. 이 부분은 내 것이 아닙니다. 메소드가 있는지 여부를 감지하기 위해 동일한 코드를 사용했습니다 (여기를 봐)

스마트 포인터로 공분산을 원하기 때문에 스마트 포인터가 기존 클래스 아키텍처를 모방하기를 원합니다. 예제에서 어떻게 작동하는지 설명하는 것이 더 쉽습니다.

A. CovariantSharedPtr<B> 정의되면 상속됩니다 ICovariantSharedPtr<B>, 그것은 다음으로 해석됩니다 ICovariantSharedPtr<B, has_parent<B>::value>. 처럼 B 상속 Child<A>, has_parent<B>::value 사실입니다 ICovariantSharedPtr<B> ~이다 ICovariantSharedPtr<B, true> 그리고 상속 ICovariantSharedPtr<B::Parent> 그게 ICovariantSharedPtr<A>. 처럼 A 없다 Parent 한정된, has_parent<A>::value 거짓, ICovariantSharedPtr<A> ~이다 ICovariantSharedPtr<A, false> 그리고 아무것도 물려받습니다.

요점은 B상속 A, 우리는 가지고 있습니다 ICovariantSharedPtr<B>상속 ICovariantSharedPtr<A>. 그래서 포인터 또는 참조를 반환하는 모든 방법 ICovariantSharedPtr<A> 동일하게 반환하는 메소드로 과부하 할 수 있습니다. ICovariantSharedPtr<B>.

깔끔한 솔루션이 게시되었습니다 이 블로그 게시물 (Raoul Borges에서)

Mulitple 상속 및 추상적 인 방법에 대한 지원을 추가하기 전에 비트의 발췌는 다음과 같습니다.

template <typename Derived, typename Base>
class clone_inherit<Derived, Base> : public Base
{
public:
   std::unique_ptr<Derived> clone() const
   {
      return std::unique_ptr<Derived>(static_cast<Derived *>(this->clone_impl()));
   }

private:
   virtual clone_inherit * clone_impl() const override
   {
      return new Derived(*this);
   }
};

class concrete: public clone_inherit<concrete, cloneable>
{
};

int main()
{
   std::unique_ptr<concrete> c = std::make_unique<concrete>();
   std::unique_ptr<concrete> cc = b->clone();

   cloneable * p = c.get();
   std::unique_ptr<clonable> pp = p->clone();
}

전체 기사를 읽는 것이 좋습니다. 그것은 단순히 쓰여지고 잘 설명되었습니다.

씨 Fooz 질문의 1 부에 답했습니다. 파트 2, 컴파일러가 컴파일 시간에 A1 :: get_r을 호출 할 것인지 알지 못하기 때문에 이런 방식으로 작동합니다. 같은 유형을 반환합니다. 이것은 C ++ 사양의 일부입니다.

해결 방법의 경우 a1 :: get_r이 retinterface로 포인터를 반환하면 retinterface의 가상 메소드가 여전히 예상대로 작동하며 포인터가 파괴 될 때 적절한 객체가 삭제됩니다. 다른 반환 유형이 필요하지 않습니다.

어쩌면 Out 매개 변수를 사용하여 "반환 된 Boost Shared_ptrs를 사용한 공분산"을 돌아 다닐 수 있습니다.

 void get_r_to(boost::shared_ptr<RetInterface>& ) ...

발신자가 인수로보다 세련된 shared_ptr 유형을 떨어 뜨릴 수 있다고 생각합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top