我正在尝试创建自定义集合MyArray<T>
的适配器
为简单起见,适配器Adapter
只做一件事:转换MyArray<T>::get
的返回结果。
(在实际情况中,MyArray
和Adapter
是非常复杂的数据库操纵器。)
这是第一个版本,它有效。 (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;
}
然后,我想牺牲性能以提高可读性和便利性。 (#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_cast
到C*
。
以下demo表明它是错误的。 (打印0而不是5)
如何改进我的第一个代码版本以使Adapter
具有更少的模板参数?
标准: -
std::function
,但它似乎是一个黑客
我还想知道是否有可能没有使用它。 Adapter<C>::setUnderlying
有效时,MyArray<X>*
的单个实例必须能够接受任何static_cast<C*>(X*)
。 MyArray
和Adapter
是图书馆类。它对T
或T2
类型没有任何了解
例如,我无法用void*
替换class MyArrayBase
中的B*
。光照条件: -
答案 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);
}
};
的基类,因为您需要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
)。
您可以在此处进行测试:http://coliru.stacked-crooked.com/a/116305ec5f18b673