스마트 포인터와 함께 공변 반환 유형을 어떻게 사용할 수 있습니까?
다음과 같은 코드가 있습니다.
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 {...}
...
};
이 코드는 컴파일되지 않습니다.
비주얼 스튜디오에서는
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
는 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
. 공변 스마트 포인터에서 사용되는 클래스는이 유형을 정의해야합니다.
The class has_parent
is used to detect at compile time if a class defines the type Parent
or not. This part is not mine, I used the same code as to detect if a method exists (see here)
As we want covariance with smart pointers, we want our smart pointers to mimic the existing class architecture. It's easier to explain how it works in the example.
When a CovariantSharedPtr<B>
is defined, it inherits from ICovariantSharedPtr<B>
, which is interpreted as ICovariantSharedPtr<B, has_parent<B>::value>
. As B
inherits from Child<A>
, has_parent<B>::value
is true, so ICovariantSharedPtr<B>
is ICovariantSharedPtr<B, true>
and inherits from ICovariantSharedPtr<B::Parent>
which is ICovariantSharedPtr<A>
. As A
has no Parent
defined, has_parent<A>::value
is false, ICovariantSharedPtr<A>
is ICovariantSharedPtr<A, false>
and inherits from nothing.
The main point is as B
inherits from A
, we have ICovariantSharedPtr<B>
inheriting from ICovariantSharedPtr<A>
. So any method returning a pointer or a reference on ICovariantSharedPtr<A>
can be overloaded by a method returning the same on ICovariantSharedPtr<B>
.
There is a neat solution posted in this blog post (from Raoul Borges)
An excerpt of the bit prior to adding support for mulitple inheritance and abstract methods is:
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();
}
I would encourage reading the full article. Its simply written and well explained.
Mr Fooz answered part 1 of your question. Part 2, it works this way because the compiler doesn't know if it will be calling AInterface::get_r or A1::get_r at compile time - it needs to know what return value it's going to get, so it insists on both methods returning the same type. This is part of the C++ specification.
For the workaround, if A1::get_r returns a pointer to RetInterface, the virtual methods in RetInterface will still work as expected, and the proper object will be deleted when the pointer is destroyed. There's no need for different return types.
maybe you could use an out parameter to get around "covariance with returned boost shared_ptrs.
void get_r_to(boost::shared_ptr<RetInterface>& ) ...
since I suspect a caller can drop in a more refined shared_ptr type as argument.
참고URL : https://stackoverflow.com/questions/196733/how-can-i-use-covariant-return-types-with-smart-pointers
'IT TIP' 카테고리의 다른 글
numpy, scipy 및 matplotlib에 대한 intersphinx 링크의 대상 지정 (0) | 2020.11.28 |
---|---|
Stack Overflow와 같은 사이트에서 검색을 구현하기 위해 Lucene.NET을 어떻게 사용합니까? (0) | 2020.11.28 |
csproj 어셈블리 참조 이해 (0) | 2020.11.28 |
스레드와 프로세스에 대해 이야기 할 때 "동시에 플로피를 포맷하지 마십시오"라는 주석이 왜 재미 있었습니까? (0) | 2020.11.28 |
MVC 모델 개체, 도메인 개체 및 DTO의 차이점은 무엇입니까? (0) | 2020.11.28 |