从Foo转换到Foo
我想了解从Foo<Derived>
到Foo<Base>
之类的转换。举个例子,如果我有下面的代码段(长,不好意思):从Foo转换<Derived>到Foo <Base>
#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个电话,可以编译成功,并得到预期的输出:
Derived::print
Derived::print const
这到底是怎么回事?为什么shared_ptr<Derived>
能够转换为const shared_ptr<Base>
和const shared_ptr<const Base>
,但不是非常量版本?另外,如何编写BasePtr
以便能够模仿shared_ptr
的行为?
编译错误,我得到如下:
'void takesBase(BasePtr<Base> &)': cannot convert argument 1 from 'BasePtr<Derived>' to 'BasePtr<Base> &'
及其组合。
您可能会认为BasePtr<Derived>
参考以某种方式轻微转换为BasePtr<Base>
参考,但事实并非如此。这两种类型是指完全无关的类,它们之间没有继承关系。他们的模板参数可能会有,但是两个模板参数之间的关系与实例化模板之间的关系没有任何关系。
设计shared_ptr
的人知道这一点,仍然希望shared_ptr
s像常规指针一样可用,并且如果它们指向的东西有父/派生关系,看起来会自动转换。
因此,它们实际上是由定义为::std::shared_ptr
成员的特殊模板转换构造函数处理的,而不是自动转换。这些创建了一个全新的shared_ptr
,它指向基本类型的一个对象。这个新对象是临时的,因此您不能将它作为非const引用参数传递。
以下是您如何为您的BasePtr
类做到这一点,尽管这很简单。查看shared_ptr
的代码,它会查找很多边界情况,在这些情况下转换将被允许或不被允许(指向数组的指针与指向单个对象的指针是其中的重要组成部分),我没有在这里进行说明。
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.
};
另外,如果你仔细想想,你想要什么有发生不会为常规指针工作,要么。
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.
}
一个重要的事情了解的模板是它们本身不是一类,他们没有与对方的关系,除非在定义
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>
仍然是全新的类,并且彼此不相关,因此它们之间的转换不会在重新解释转换之后良好转换h不建议。
编辑:在你的努力做
多一点信息,你需要做的,让这种转换将是例子缘故模板转换功能,下面有一个未经测试的尝试什么
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一起工作,但它在模板类型之间转换后的一般想法
您在调用ptr_(std :: make_shared())时调用派生函数的原因是什么构造函数? –