集合适配器 - 省略取决于底层集合的T.

时间:2017-04-21 06:21:06

标签: c++ templates data-structures c++14 virtual-functions

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

(在实际情况中,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
目标:将模板参数的数量从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具有更少的模板参数?

标准: -

  • 不要使用功能指针 我觉得有可能使用函数指针或std::function,但它似乎是一个黑客 我还想知道是否有可能没有使用它。
  • 开销不应(大致)比第2版中的单个虚拟呼叫(v-table)差。
  • Adapter<C>::setUnderlying有效时,MyArray<X>*的单个实例必须能够接受任何static_cast<C*>(X*)
  • MyArrayAdapter是图书馆类。它对TT2类型没有任何了解 例如,我无法用void*替换class MyArrayBase中的B*

光照条件: -

  • 我更喜欢使用虚拟功能的解决方案。
  • 如果没有虚拟成本,那将是理想的,但我不认为这是可能的。

1 个答案:

答案 0 :(得分:2)

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

// 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类型(要转换为的类型)和只知道{{1}的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); } }; 的基类,因为您需要MyArraysetUnderlying中推断出类型B

MyArray<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)。

您可以在此处进行测试:http://coliru.stacked-crooked.com/a/116305ec5f18b673