从 Foo<Derived> 到 Foo <Base>的转换
Conversions from Foo<Derived> to 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 constn"; }
virtual void print() override { std::cout << "Derived::printn"; }
};
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
可以像常规指针一样使用,并且如果他们所指向的对象具有父/派生关系,则似乎可以自动转换。
因此,这些转换不是自动的,而是由定义为::std::shared_ptr
成员的特殊模板转换构造函数来处理。这些创建了一个指向基本类型对象的全新shared_ptr
。这个新对象是一个临时对象,因此您不能将其作为非常数引用参数传递。
以下是您为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>
仍然是全新的类,彼此不相关,因此除了不建议的重新解释类型转换之外,它们之间的转换都不好。
编辑:关于你尝试做什么的更多信息
为了允许这种转换,你需要做的是一个模板转换功能,例如未经测试的尝试
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一起工作,但这是在模板类型之间转换的一般想法
- 请解释这句话(cout<<1+int((a<b)^((b-a)&1) )<<endl
- 呼叫运营商<<临时
- 如何防止clang格式在流运算符调用之间添加换行符<<
- <<操作员在下面的行中工作
- EASTL矢量<向量<int>>连续的
- C - 创建矢量&lt; vector&lt; double&gt;&gt;矩阵具有分配而不是inizializ
- 为什么将此对向量&lt; map&lt; int,int&gt;&gt;中的地图进行更新.失败
- C :对矢量进行排序&lt; struct&gt;(结构有2个整数)基于结构的整数之一
- 明确的专业化“ CheckIntmap&lt;&gt;”实例化
- 什么是模板&lt;&gt;inline bla bla
- 编辑C Qlist&lt; object*&gt; gt;QML代码和一些QML警告中的模型
- eigen :: llt&lt;eigen :: matrixxd&gt;具有不完整的类型
- 错误,包括&lt; ctype&gt;在原子上使用C 11
- std::vector<;uint8_t>;当C++11/14启用时,手动复制而不是调用memcpy
- 如何加入向量&lt; int&gt;到C 中的单个INT
- 是std :: set&lt; std :: future&gt;不可能存在
- 是numeric_limits&lt; int&gt; :: is_modulo从逻辑上矛盾
- opencv 2.4.7在iOS错误背景_segm.hpp #include&lt; list&gt;未找到
- 在修改列表后,std :: list&lt; t&gt; :: end()的值是否会更改
- ///<评论></评论>在Visual Studio中