스마트 포인터와 함께 공분산 반환 유형을 어떻게 사용할 수 있습니까?
-
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_ptr
의 Ret1
파생되지 않습니다 boost::shared_ptr
의 RetInterface
. 그러나 나는 돌아오고 싶다 boost::shared_ptr
의 Ret1
다른 클래스에서 사용하려면 반품 후 반환 된 값을 시전해야합니다.
- 내가 뭔가 잘못하고 있습니까?
- 그렇지 않다면 왜 이런 언어가 이와 같은 언어 인 경우 -이 시나리오에서 스마트 포인터 간의 전환을 처리하는 것이 확장 가능해야합니까? 바람직한 해결 방법이 있습니까?
해결책
첫째, 이것은 실제로 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 유형을 떨어 뜨릴 수 있다고 생각합니다.