78

我有这样的代码:

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_ptrof派生的。但是我想返回用于其他类,否则我必须在返回后转换返回值。 Ret1boost::shared_ptrRetInterfaceboost::shared_ptrRet1

  1. 难道我做错了什么?
  2. 如果不是,为什么这种语言是这样的——在这种情况下处理智能指针之间的转换应该是可扩展的?是否有理想的解决方法?
4

7 回答 7

31

首先,这确实是它在 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 {...}
     ...
};

或者,您可以使用访问者模式或类似我的Dynamic Double Dispatch技术将回调传递给返回的对象,然后该对象可以调用具有正确类型的回调。

于 2008-10-13T09:58:40.397 回答
4

此博客文章中发布了一个简洁的解决方案(来自 Raoul Borges)

在添加对多重继承和抽象方法的支持之前的一段摘录是:

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

我鼓励阅读全文。它写得很简单,解释得很好。

于 2019-06-11T11:24:38.420 回答
2

在 C++ 中重载方法时,您不能更改返回类型(对于非指针、非引用返回类型)。 A1::get_r必须返回一个boost::shared_ptr<RetInterface>.

安东尼威廉姆斯有一个很好的综合答案

于 2008-10-13T04:49:45.013 回答
1

这个解决方案怎么样:

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_; }
};
于 2013-04-17T20:19:36.287 回答
1

这是我的尝试:

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并定义TParent. 协变智能指针中使用的类需要定义这种类型。

该类has_parent用于在编译时检测一个类是否定义了类型Parent。这部分不是我的,我使用了与检测方法是否存在相同的代码(请参见此处

由于我们想要与智能指针的协变,我们希望我们的智能指针模仿现有的类架构。在示例中更容易解释它是如何工作的。

当 aCovariantSharedPtr<B>被定义时,它继承自ICovariantSharedPtr<B>,它被解释为ICovariantSharedPtr<B, has_parent<B>::value>。AsB继承自Child<A>,has_parent<B>::value是真的,所以ICovariantSharedPtr<B>isICovariantSharedPtr<B, true>并且继承自ICovariantSharedPtr<B::Parent>which is ICovariantSharedPtr<A>。AsA没有Parent定义,has_parent<A>::value是假的,ICovariantSharedPtr<A>ICovariantSharedPtr<A, false>并且从无继承。

重点是作为B继承自A,我们有ICovariantSharedPtr<B>继承自ICovariantSharedPtr<A>。因此,任何返回指针或引用 onICovariantSharedPtr<A>的方法都可以被返回相同 on 的方法重载ICovariantSharedPtr<B>

于 2015-09-11T17:27:38.367 回答
-1

福兹先生回答了您问题的第 1 部分。第 2 部分,它以这种方式工作,因为编译器不知道它是否会在编译时调用 AInterface::get_r 或 A1::get_r - 它需要知道它将获得什么返回值,因此它坚持使用这两种方法返回相同的类型。这是 C++ 规范的一部分。

对于解决方法,如果 A1::get_r 返回一个指向 RetInterface 的指针,则 RetInterface 中的虚拟方法仍将按预期工作,并且在销毁指针时将删除正确的对象。不需要不同的返回类型。

于 2008-10-13T05:00:11.043 回答
-2

也许您可以使用 out 参数来解决“与返回的 boost shared_ptrs 的协方差。

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

因为我怀疑调用者可以使用更精致的 shared_ptr 类型作为参数。

于 2009-06-09T13:26:04.450 回答