我有一个方法
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的开销(因此每个容器需要一个锁而不是容器中每个对象一个)。
再次感谢,你们都很棒!
答案 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);
}