2

我有一个方法

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

4 回答 4

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());

一切都应该正常工作。

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

于 2010-05-06T15:14:10.373 回答
4

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

您可以使用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()));
于 2010-05-06T15:10:34.857 回答
1

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

考虑foo尝试将类型的新元素添加BasemyList. 如果您可以myList1转换为 a list<shared_ptr<Base>>(例如,使用旧式 C 转换),并将其传递给foo,结果不会很好。

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

于 2010-05-06T15:09:07.417 回答
0

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

如果你有一个list<Apple*>很好的苹果,并且你能够将它传递给一个接受 a 的函数list<Fruit*>,那么就不会阻止函数将新的橘子放入列表中。Alist<Fruit*>根本不能替代 alist<Apple*>并且不存在任何关系。

如果您想要一个可以与 alist<Apple*>和一起使用的函数list<Orange*>,您可以使用模板(无论是使用迭代器,还是使用 Container 本身,或者list<T*>完全是您的选择。


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

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

#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);
}
于 2010-05-06T15:55:49.493 回答