集合适配器 - 省略依赖底层集合的 T

Adapter of collection - omit T that depends on underlying collection

我正在尝试创建自定义集合的适配器 MyArray<T>
为简单起见,适配器 Adapter 只做一件事:转换 MyArray<T>::get 的 return 结果。

(在实际情况下,MyArrayAdapter 是非常复杂的数据库操纵器。)

版本 1

这是第一个版本,可以用。 (demo)

#include <iostream>
using namespace std;
template<class T>class MyArray{
    public: T* database[20];   
    public: T* get(int index){return database[index];} //<-important
    public: void set(int index,T* t){database[index]=t;} 
};
template<class T,class T2> class Adapter{
    public: MyArray<T>* underlying;
    public: void setUnderlying(MyArray<T>* pUnder){underlying=pUnder;}
    public: T2* get(int index){return static_cast<T2*>(underlying->get(index));}
    //^ "Adapter::get()" is encapsulating "MyArray::get()"
};
class B{};
class C:public B{}; 
class D:public C{};

int main() {
    MyArray<B> bs;
    bs.set(0,new C());  //some can be new D()
    //About the Adapter<C>, user is the one who sure that "bs" elements are "C*"-castable.
    Adapter<B,C> cs;       //<-- #1 need improve
    cs.setUnderlying(&bs); //<-- assign MyArray* to adapter
    C* c=cs.get(0);
    return 0;
}

版本 2

那么,为了可读性和方便性,我想牺牲性能。 (#1)
Objective: 将模板参数的数量从 2 (Adapter<B,C>) 减少到 1 (Adapter<C>)。

这是我到目前为止的工作。它是可编译的,但在某些情况下应该会崩溃:-

class MyArrayBase{  //<--- new class
     public: virtual void* get(int index)=0;
};
template<class T>class MyArray : public MyArrayBase{
    public: T* database[20];
    public: T* get(int index){return database[index];}
    public: void set(int index,T* t){database[index]=t;}
};
template<class T2> class Adapter{
    public: MyArrayBase* underlying;  //<--- more abstraction 
    public: void setUnderlying(MyArrayBase* pUnder){underlying=pUnder;}
    public: T2* get(int index){return static_cast<T2*>(underlying->get(index));}   //#wrong
};
class B{};
class C:public B{};

int main() {
    MyArray<B> bs;
    bs.set(0,new C()); 
    Adapter<C> cs;   //<--- Yes! 1 template argument.
    cs.setUnderlying(&bs);
    C* c=cs.get(0);
    std::cout<<"hi"<<std::endl;
    return 0;
}

错误原因:-
#wrongvoid*(底层B*)是static_castC*
这里的demo说明是错误的。 (打印 0 而不是 5)

问题

如何改进我的第一个版本的代码以减少 Adapter 的模板参数?

条件:-

灯光标准:-

您可以使用某种包装容器的包装器,通常是:

// Here T = T2, you want a virtual function that already give you the right type
template <typename T>
class Wrapper {
public:
    virtual T* get(int index) const = 0;
};

// The real wrapper: It can give you T2 (To) but keep information
// about the original type since it is templated on Container
template <class To, class Container>
class WrapperContainer: public Wrapper<To> {
    Container *cont_;
public:  
    WrapperContainer(Container *cont) : cont_(cont) { }
    virtual To* get(int index) const override {
        return static_cast<To*>(cont_->get(index));
    }
};

包装器是你的 Adapter 之间的中间人,它只知道 To 类型(你想转换成的类型)和你的 MyArray 只知道 From 类型(您要转换的类型)- WrapperContainer 知道两者,因此它可以在可能的情况下安全地从一种类型转换为另一种类型。

决赛Adapter:

template<class T2> 
class Adapter {
    std::unique_ptr<Wrapper<T2>> w_; 
public:

    template <typename Container>
    void setUnderlying(Container *cont) {
        w_ = std::unique_ptr<Wrapper<T2>>(new WrapperContainer<T2, Container>(cont));
    }

    T2* get(int index)  {
        return w_->get(index);
    }
};

使用这个你不需要 MyArray 的基础 class 因为你需要 setUnderlyingMyArray<B> 推导出类型 B:

// No more need for a base class
template<class T>
class MyArray {
    T* database[20];
public: 
    T* get(int index){return database[index];}
    void set(int index,T* t){database[index]=t;}
};

您的代码的重要变化实际上是这一行:

return static_cast<To*>(cont_->get(index));

cont_->get(index) 的类型是 B*(在本例中)而不是 void*,这使得转换有效。这也可以防止将 setUnderlying 与不兼容类型的数组一起使用(尝试取消注释下面代码中的 cs.setUnderlying(&as); 行)。

你可以在这里测试:http://coliru.stacked-crooked.com/a/116305ec5f18b673