是否有一种简单(通用)的方法,使std::unique_ptr
的容器显示为原始指针的容器(特别是在基于循环或算法的范围中使用时)?
背景
在查看一些代码时,我遇到了以下模式的几个实例:
using namespace std;
struct A {
virtual void foo() const = 0;
virtual ~A() {}
};
struct B1 : A { void foo() const { cout << "Called B1::foo()" << endl; } };
struct B2 : A { void foo() const { cout << "Called B2::foo()" << endl; } };
class CustomList {
vector<A*> list;
public:
void addB1() { list.push_back(new B1{}); }
void addB2() { list.push_back(new B2{}); }
const auto& getList() const { return list; }
~CustomList() {
for (size_t i = 0; i < list.size(); ++i) {
delete list[i];
}
}
};
void useA(const A* a) { a->foo(); }
void useList() {
CustomList cl; cl.addB1(); cl.addB2();
for (const auto& a : cl.getList()) {
useA(a);
}
}
为了简化,我想将vector<A*>
替换为vector<unique_ptr<A>>
,但不幸的是,getList()
泄漏了该实现细节,因此useA(a)
(以及整个程序中的类似行)必须由useA(a.get())
取代,这非常令人讨厌并且与简化目标相矛盾。
现在,我基本上可以想到两个解决方案:
begin
,end
和operator[]
等适当的函数添加到CustomList
类(有多个不同的类),这样就不必公开内部容器类。 getList()
返回的代理类。 但是,这两种情况都需要对程序和转换迭代器进行一些更改,我开始怀疑unique_ptr
的引入是否引入了比删除更复杂的复杂性。
所以我想知道是否有第三个更简单的解决方案。
注:
useA
所代表的函数的界面。首先,它们中有相当多,其次,它们的使用不仅限于这种特定模式,因此界面更改会强制更改代码中完全不相关的部分。供参考,这是我的代理版本(欢迎任何建议):
template<class BaseIterator>
class TransformIterator: public BaseIterator {
public:
/* ## override basic type definitions ## */
using value_type = typename BaseIterator::value_type::pointer;
using pointer = value_type*;
using reference = value_type&;
TransformIterator(const BaseIterator& other) : BaseIterator{ other } {}
/* ## override/hide member functions, that dereferenciate the iterator ## */
value_type operator*() const { return this->BaseIterator::operator*().get(); }
value_type operator[](size_t n) const { return this->BaseIterator::operator[](n).get(); }
//overriding operator->() is not necessary, as value type (a pointer) isn't a struct or class
};
template<class CONTAINER>
class ContainerProxy {
const CONTAINER& _list;
public:
ContainerProxy(const CONTAINER& list) : _list{ list } {}
TransformIterator<vector<unique_ptr<A>>::const_iterator> begin() const { return _list.begin(); }
TransformIterator<vector<unique_ptr<A>>::const_iterator> end() const { return _list.end(); }
/* ...
* other container functions like size, operator[] and conversion operator (operator const CONTAINER&())
*/
};
class CustomList2 {
vector<unique_ptr<A>> list;
public:
void addB1() { list.push_back(make_unique<B1>()); }
void addB2() { list.push_back(make_unique<B2>()); }
ContainerProxy<vector<unique_ptr<A>>> getList() const { return list; }
};