从指针容器切换到唯一指针容器

时间:2015-08-23 00:08:10

标签: c++ iterator containers adapter

是否有一种简单(通用)的方法,使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())取代,这非常令人讨厌并且与简化目标相矛盾。

现在,我基本上可以想到两个解决方案:

  1. beginendoperator[]等适当的函数添加到CustomList类(有多个不同的类),这样就不必公开内部容器类。
  2. 编写可由getList()返回的代理类。
  3. 但是,这两种情况都需要对程序和转换迭代器进行一些更改,我开始怀疑unique_ptr的引入是否引入了比删除更复杂的复杂性。

    所以我想知道是否有第三个更简单的解决方案

    注:

    • 我不想使用boost :: transform_iterator,因为该项目目前没有依赖boost(程序在嵌入式系统上运行)。
    • 我也不想像this question那样创建一个带有指向存储在列表中的对象的指针的实际容器,因为这会引入额外的动态内存分配,而且我通常也只需要访问一些项目从列表中。
    • 最后,我也不想改变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; }
    };
    

0 个答案:

没有答案