转换shared_ptr的容器

时间:2010-05-06 15:05:27

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

我有一个方法

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的开销(因此每个容器需要一个锁而不是容器中每个对象一个)。

再次感谢,你们都很棒!

4 个答案:

答案 0 :(得分:7)

更改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<T1*>&来自std::list<T2*>,也无法使用T2初始化T1

答案 1 :(得分:3)

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

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

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

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

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

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

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

答案 2 :(得分:1)

请注意list<shared_ptr<Base>>list<shared_ptr<DerivedClass1>>(甚至shared_ptr<Base>shared_ptr<DerivedClass1>)是完全不同的类型,即使DerivedClass1继承自Base 。因此,将一个投射到另一个实际上是错误的。

考虑foo尝试将Base类型的新元素添加到myList的情况。如果您可以将myList1投射到list<shared_ptr<Base>>(例如使用旧式C演员),并将其传递给foo,结果就不会很好。

所以我看到的唯一合适的解决方案是创建一个新的list<shared_ptr<Base>>对象并将其他列表的内容复制到其中,然后将其传递给foo,如@James McNellis所示。

答案 3 :(得分:0)

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

如果你有一个list<Apple*>很好地填充了苹果,并且你能够将它传递给接受list<Fruit*>的函数,那么就没有停止该函数将新的橙子放入列表中。 list<Fruit*>根本不能代替list<Apple*>而且没有关系。

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


如果你真的非常想传递一个list<Apple*>,就像list<Fruit*>一样,那么我认为在技术上可以为容器/迭代器提供代理,使用类型擦除和什么 - 不要在内部施放指针/停止无效的强制转换(将橙子放入苹果列表中),这样你就可以使用list_proxy<Fruit*>接受派生类型列表。{/ p>

某些事情(仅仅是一个开头):

#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);
}
相关问题