首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >从Foo<Derived>到Foo<Base>的转换

从Foo<Derived>到Foo<Base>的转换
EN

Stack Overflow用户
提问于 2017-10-16 20:26:14
回答 2查看 98关注 0票数 1

我正在尝试理解从Foo<Derived>Foo<Base>的转换。例如,如果我有以下代码片段(长,对不起):

代码语言:javascript
复制
#include <iostream>
#include <memory>
#include <vector>

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

class Derived : public Base
{
public:
  virtual void print() const override { std::cout << "Derived::print const\n"; }
  virtual void print() override { std::cout << "Derived::print\n"; }
};

template <typename R>
class BasePtr
{
public:
  BasePtr() : ptr_(std::make_shared<R>())
  {
  }

  void print() const
  {
    ptr_->print();
  }

private:
  std::shared_ptr<R> ptr_;
};

/* Takes const shared ptr */
void takesConstSharedPtr(const std::shared_ptr<Base>& base)
{
  base->print();
}

void takesConstSharedPtrConst(const std::shared_ptr<const Base>& base)
{
  base->print();
}

/* Takes non-const shared ptr */
void takesSharedPtr(std::shared_ptr<Base>& base)
{
  base->print();
}

void takesSharedPtrConst(std::shared_ptr<const Base>& base)
{
  base->print();
}

/* Takes base ptr class */
void takesBase(BasePtr<Base>& base)
{
  base.print();
}

void takesBaseConst(BasePtr<const Base>& base)
{
  base.print();
}

/* Takes const base ptr class */
void takesConstBase(const BasePtr<Base>& base)
{
  base.print();
}

void takesConstBaseConst(const BasePtr<const Base>& base)
{
  base.print();
}

int main()
{
  std::shared_ptr<Derived> ptr = std::make_shared<Derived>();
  BasePtr<Derived> basePtr;

  // Works!
  takesConstSharedPtr(ptr);
  takesConstSharedPtrConst(ptr);

  // Does not works...
  takesSharedPtr(ptr);
  takesSharedPtrConst(ptr);

  takesConstBase(basePtr);
  takesConstBaseConst(basePtr);

  takesBase(basePtr);
  takesConstBase(basePtr);
}

我在主函数中得到最后6个调用的编译错误,但在前两个调用中没有编译错误。如果我注释掉最后6个调用,我可以成功编译并获得预期的输出:

代码语言:javascript
复制
Derived::print
Derived::print const

这里发生什么事情?为什么shared_ptr<Derived>可以转换为const shared_ptr<Base>const shared_ptr<const Base>,而不能转换为非const版本?另外,如何编写BasePtr来模拟shared_ptr的行为?

我得到的编译错误如下:

代码语言:javascript
复制
'void takesBase(BasePtr<Base> &)': cannot convert argument 1 from 'BasePtr<Derived>' to 'BasePtr<Base> &'

以及它们的组合。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2017-10-16 20:30:40

您可能会认为BasePtr<Derived>引用在某种程度上是可以转换为BasePtr<Base>引用的,但事实并非如此。这两种类型指的是它们之间没有继承关系的完全无关的类。它们的模板参数可以,但是两个模板参数之间的关系与实例化模板之间的关系没有任何关系。

设计shared_ptr的人知道这一点,并且仍然希望shared_ptr能够像常规指针一样可用,并且如果指向的东西有父/派生关系,那么似乎可以自动转换。

因此,这些转换不是自动的,而是由定义为::std::shared_ptr成员的特殊模板转换构造函数处理的。它们创建了一个全新的shared_ptr,它指向基类型的对象。这个新对象是一个临时对象,因此不能将它作为非const引用参数传递。

下面是如何对您的BasePtr类执行此操作,尽管这很简单。看一看shared_ptr的代码,它寻找了很多允许或不允许转换的边缘情况(指向数组的指针和指向单个对象的指针是其中的很大一部分),我在这里没有考虑到这些情况。

代码语言:javascript
复制
template <typename T>
class BasePtr {
  public:
   BasePtr() : ptr_(new T) { }

   // This conversion constructor will fail if other.ptr_ cannot be
   // assigned to ptr_ without any explicit conversion.
   template <typename U>
   BasePtr(const BasePtr<U> &other) : ptr_(other.ptr_) { }

   // You would also need a converting version of operator = that
   // was written in much the same way.
};

而且,如果你仔细想想,你想要发生的事情也不会适用于常规指针。

代码语言:javascript
复制
class Base {
};

class Derived : public Base {
};

void foo(Base *&baz)
{
}

void bar(Base * const &qux)
{
}

void trivially_works(Derived *&d)
{
}

void testing()
{
    Derived *d = new Derived;
    foo(d); // Failed with a compiler error, for same reason as BasePtr<Base> &.
    bar(d); // Works for same reason as const BasePtr<Base> &
    trivially_works(d); // And this works because you're passing a reference to d, not a temporary.

    Base *b = d; // Works of course
    foo(b); // And now this works because it's a reference to b, not a temporary.
}
票数 4
EN

Stack Overflow用户

发布于 2017-10-16 20:38:06

关于模板,需要了解的一件重要事情是,它们本身不是一个类,除非在定义中明确声明,否则它们之间没有关系。

代码语言:javascript
复制
template<typename T> class Foo; // not a class, only a template to make a class from
typedef Foo<Bar> FooBar; // an entirely new class with no relationships

即使我们有一个基类BarA和一个子类BarB,从Foo生成的任何类仍然是一个新类,因此Foo<BarA>Foo<BarB>仍然是全新的类,彼此之间没有任何关联,因此在重新解释不建议的强制转换之外,它们之间的任何转换都是好的。

编辑:更多关于你想做什么的信息

为了允许这种转换,您需要做的是一个模板转换函数,例如,这里是一个未经测试的尝试。

代码语言:javascript
复制
template<typename T> class Foo {

    Foo(T* const t_contained) {

        // set up stuff
    }

    template<typename T_CONVERT_TO>
    FOO<T_CONVERT_TO> asFoo() {

        // assuming m_contained can convert to Foo<T_CONVERT_TO>'s m_contained
        // if not it'll complain, so you know asFoo can only be given covnertable values
        return Foo<T_CONVERT_TO>( m_contained );
    }
};

Foo<BarB> fooBarB;
Foo<BarA> fooBarA = fooBarB.asFoo<BarB>();

同样,没有经过测试,我也不知道它是否适用于shared_ptr,但这是在模板类型之间进行转换的一般想法。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/46778561

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档