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

标签 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;
}

错误的原因:-
#wrong , void* (基础 B* )是 static_castC* .
这是 demo表明这是错误的。 (打印 0 而不是 5)

问题

如何改进我的第一个版本的代码来制作Adapter有更少的模板参数?

标准:-

  • 不要使用函数指针。
    我觉得可以使用函数指针或 std::function , 但它似乎是一个 hack。
    我还想知道是否可以使用它。
  • 开销不应(大致)比版本 2 中的单个虚拟调用(v 表)差。
  • Adapter<C>::setUnderlying 的单个实例必须能够接受任何 MyArray<X>*什么时候打电话有意义static_cast<C*>(X*) .
  • MyArrayAdapter是图书馆类。它不知道类型 TT2 .
    例如,我无法替换 void*class MyArrayBaseB* .

光标准:-

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

最佳答案

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

// 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 的基类因为你需要 setUnderlying推断类型 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使用不兼容类型的数组(尝试取消注释下面代码中的行 cs.setUnderlying(&as);)。

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

关于c++ - 集合适配器 - 省略依赖底层集合的 T,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43535308/

相关文章:

javascript - 使用下划线外部模板在主干中设置模型内容

c - 查找从给定的起始节点到结束节点的所有可能路径

c++ - C++中的按位设置

c++ - 如何在 C++ 中查找内存泄漏

c++ - 使用可变参数模板的显式模板实例化

c++ - C++中非模板类的特化成员函数

c++ - 输出二叉搜索树中叶节点的数量

等效的 JavaScript HashMap

c++ - 需要 OpenGL/PBO 像素绘图示例

c++ - 如何对重载运算符使用 gmock MOCK_METHOD?