【问题标题】:Casting a container of shared_ptr投射 shared_ptr 的容器
【发布时间】:2010-05-06 15:05:26
【问题描述】:

我有办法

void foo(list<shared_ptr<Base>>& myList); 

我试图用两种不同类型的列表调用,一种是 DerivedClass1,一种是 DerivedClass2

list<shared_ptr<DerivedClass1>> myList1; 
foo(myList1);
list<shared_ptr<DerivedClass2>> myList2; 
foo(myList2);

然而这显然会产生编译错误

error: a reference of type "std::list<boost::shared_ptr<Base>, std::allocator<boost::shared_ptr<Base>>> &" (not const-qualified) cannot be initialized with a value of type "std::list<boost::shared_ptr<DerivedClass1>, std::allocator<boost::shared_ptr<DerivedClass1>>>"

有没有什么简单的方法来投射 shared_ptr 的容器?有哪些替代容器可以做到这一点?

更新:感谢所有回复的人。在语言的范围内工作,保持方法“原样”的最佳方法似乎是使用 shared_ptr 的容器并将其准确传递(在调用站点创建一个新列表)。

我想我几乎已经知道这一点,但我记得读过关于处理 shared_ptr 容器的 boost 库的其他部分,并认为它可能已经被其他人更优雅地解决了。然而,从我自己的进一步研究来看,这些似乎更倾向于减少 shared_ptr 的开销,在一些指针被独占拥有的情况下(因此需要每个容器一个锁,而不是容器中的每个对象一个锁)。

再次感谢你们,你们都很棒!

【问题讨论】:

    标签: c++ boost casting containers shared-ptr


    【解决方案1】:

    更改 foo 使其遵循 STL 约定并采用迭代器:

    template< typename It >
    void foo(It begin, It end); 
    

    然后将迭代器传递到您的列表中

    list<shared_ptr<DerivedClass1>> myList1;
    foo(myList1.begin(), myList1.end());
    list<shared_ptr<DerivedClass2>> myList2;
    foo(myList2.begin(), myList2.end());
    

    一切都应该正常工作。

    编辑
    请注意,这对于智能指针来说并没有什么特别之处。 std::list&lt;T1*&gt;&amp; 不能用 std::list&lt;T2*&gt; 初始化,即使 T2 派生自 T1

    【讨论】:

    • 但是在其他情况下,您必须使用模板函数来处理基本继承可以很好地处理的事情,这不是很奇怪吗?
    • @Jamie Cook:不。在任何情况下,基本继承都不会处理这种情况。 Derived 的容器不是 Base 的容器。如果允许,那么您将能够将 Base 类型的元素添加到函数内的该容器中,从而破坏类型系统。
    • @Jamie: T&lt;A&gt;T&lt;B&gt; 对于任何模板 T 和任何不同的 AB 都是完全不同的类,无论 A 和 B 如何相关。跨度>
    • @Potatoswatter:一般来说不正确,具有讽刺意味的是 shared_ptr 本身就是一个这样的例子。一般来说,一些模板类选择允许通过特殊的模板化复制构造函数进行此类转换。
    • @Jamie:迭代器是……嗯,迭代的最小接口。容器允许您使用一个参数而不是两个参数,但无论如何您仍然必须在其内部调用begin()end()。就迭代器而言的函数适用于任何容器,以及容器内的范围、C 样式数组、I/O 流、生成器函数等。
    【解决方案2】:

    您不能将一种类型的容器转换为另一种类型的容器。如果现有容器存储的对象类型可以转换为新容器存储的对象类型,则有几种方法可以从现有容器创建新容器:

    您可以使用std::copy进行逐个元素的转换:

    list<shared_ptr<Base> > baseList;
    list<shared_ptr<Derived> > derivedList;
    std::copy(derivedList.begin(), derivedList.end(), std::back_inserter(baseList));
    

    您也可以使用derivedList 的开始和结束迭代器直接构造baseList

    list<shared_ptr<Base> > baseList(derivedList.begin(), derivedList.end());
    

    如果你的函数可以取一个 const 引用,你可以在函数调用中构造一个临时的:

    typedef list<shared_ptr<Base> > BaseList;
    foo(BaseList(derivedList.begin(), derivedList.end()));
    

    【讨论】:

    • list 有一个基于范围的构造函数时,为什么还要使用copy
    • 如第二个例子所示?
    【解决方案3】:

    请注意list&lt;shared_ptr&lt;Base&gt;&gt;list&lt;shared_ptr&lt;DerivedClass1&gt;&gt;(甚至shared_ptr&lt;Base&gt;shared_ptr&lt;DerivedClass1&gt;)是完全不同的类型,即使DerivedClass1 继承自Base。因此,将一个转换为另一个实际上是错误的。

    考虑foo 尝试将Base 类型的新元素添加到myList 的情况。如果您可以将 myList1 转换为 list&lt;shared_ptr&lt;Base&gt;&gt;(例如使用旧式 C 转换),然后将其传递给 foo,结果将不会很好。

    所以我看到的唯一正确的解决方案是创建一个新的list&lt;shared_ptr&lt;Base&gt;&gt; 对象并将另一个列表的内容复制到其中,然后将其传递给foo,正如@James McNellis 所展示的那样。

    【讨论】:

    • 但仍然......如果方法 foo 采用单个元素,那么您将能够调用 foo(static_pointer_cast(objectOfTypeDerivedClass1))
    • @Jamie Cook,是的,但这是一个不同的问题(我澄清了我的帖子)。顺便说一句,static_pointer_cast 是非标准的 VC++ 扩展吗?
    • @Jamie:是的,shared_ptr&lt;Base&gt;shared_ptr&lt;Derived&gt; 之间有特殊规则,因为shared_ptr 模拟一个指针。然而,不同的容器是完全不同的。如果您希望相同的函数适用于不同类型的容器,请使用模板(编译时多态),或存储指向基址的指针并使用运行时多态。
    • shared_ptr&lt;&gt; 混淆了这个问题。您也不能将list&lt;Base*&gt; 转换为list&lt;Derived*&gt;。就编译器而言,它们是不相关的类型。
    • 是的,我想这是一个不同的问题......也许我应该澄清一下参数列表将是 const。但我认为这不会改变任何有意义的事情,编译器仍然无法进行任何简单的转换。 boost::static_pointer_cast 来自 boost::shared_ptr 并且基本上做到了它所说的(还有 boost::dynamic_pointer_cast)
    【解决方案4】:

    为什么这不是一个好主意的经典解释如下:

    如果您有一个很好地填充了苹果的list&lt;Apple*&gt;,并且您能够将它传递给接受list&lt;Fruit*&gt; 的函数,那么就不会阻止该函数将新的橙子放入列表中。 list&lt;Fruit*&gt; 根本无法替代 list&lt;Apple*&gt; 并且不存在任何关系。

    如果您想要一个可以同时使用list&lt;Apple*&gt;list&lt;Orange*&gt; 的函数,您可以使用模板(无论是使用迭代器,还是使用容器本身,或者使用list&lt;T*&gt; - 完全由您选择。


    如果你真的很想传递 list&lt;Apple*&gt; 就像传递 list&lt;Fruit*&gt; 一样,那么我想在技术上可以为容器/迭代器提供代理,使用类型擦除和不强制转换内部指针/停止无效转换(将 Oranges 放入 Apples 列表中),以便您可以使该函数改为采用接受派生类型列表的 list_proxy&lt;Fruit*&gt;

    类似于(只是一个开始):

    #include <list>
    #include <boost/shared_ptr.hpp>
    
    template <class T>
    class ListWrapperBaseAux
    {
    public:
        virtual ~ListWrapperBaseAux() {}
        virtual T* front() = 0;
    };
    
    template <class T, class U>
    class ListWrapperAux: public ListWrapperBaseAux<T>
    {
        std::list<U*>* list_ref;
    public:
        ListWrapperAux(std::list<U*>* list_ref): list_ref(list_ref) {}
        virtual T* front() { return dynamic_cast<T*>(list_ref->front()); }
    };
    
    template <class T>
    class ListProxy
    {
        boost::shared_ptr<ListWrapperBaseAux<T> > list_ref;
    public:
        template <class U>
        ListProxy(std::list<U*>& li): list_ref(new ListWrapperAux<T, U>(&li)) {}
        T* front() { return list_ref->front(); }
    };
    
    struct Fruit
    {
        virtual ~Fruit() {}
    };
    
    struct Apple: Fruit {};
    struct Orange: Fruit {};
    
    void bake(ListProxy<Fruit> li)
    {
        Fruit* f = li.front();
    }
    
    int main()
    {
        std::list<Apple*> apples;
        bake(apples);
    
        std::list<Orange*> oranges;
        bake(oranges);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-03-01
      • 2015-12-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-26
      • 1970-01-01
      • 2010-09-11
      相关资源
      最近更新 更多