集合适配器 - 省略依赖底层集合的 T
Adapter of collection - omit T that depends on underlying collection
我正在尝试创建自定义集合的适配器 MyArray<T>
。
为简单起见,适配器 Adapter
只做一件事:转换 MyArray<T>::get
的 return 结果。
(在实际情况下,MyArray
和 Adapter
是非常复杂的数据库操纵器。)
版本 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;
}
错误原因:-
在#wrong
,void*
(底层B*
)是static_cast
到C*
。
这里的demo说明是错误的。 (打印 0 而不是 5)
问题
如何改进我的第一个版本的代码以减少 Adapter
的模板参数?
条件:-
- 不要使用函数指针。
我觉得用函数指针或std::function
是可能的,但它似乎是一个hack。
我还想知道是否可以 不 使用它。
- 开销不应(大致)比版本 2 中的单个虚拟调用 (v-table) 差。
- 当调用
static_cast<C*>(X*)
有意义时,Adapter<C>::setUnderlying
的单个实例必须能够接受任何 MyArray<X>*
。
MyArray
和 Adapter
是库 class。它不知道类型 T
或 T2
.
例如,我无法将 class MyArrayBase
中的 void*
替换为 B*
。
灯光标准:-
- 我更喜欢使用虚函数的解决方案。
- 如果没有虚拟成本是最理想的,但我认为这是不可能的。
您可以使用某种包装容器的包装器,通常是:
// 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 因为你需要 setUnderlying
从 MyArray<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
我正在尝试创建自定义集合的适配器 MyArray<T>
。
为简单起见,适配器 Adapter
只做一件事:转换 MyArray<T>::get
的 return 结果。
(在实际情况下,MyArray
和 Adapter
是非常复杂的数据库操纵器。)
版本 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;
}
错误原因:-
在#wrong
,void*
(底层B*
)是static_cast
到C*
。
这里的demo说明是错误的。 (打印 0 而不是 5)
问题
如何改进我的第一个版本的代码以减少 Adapter
的模板参数?
条件:-
- 不要使用函数指针。
我觉得用函数指针或std::function
是可能的,但它似乎是一个hack。
我还想知道是否可以 不 使用它。 - 开销不应(大致)比版本 2 中的单个虚拟调用 (v-table) 差。
- 当调用
static_cast<C*>(X*)
有意义时,Adapter<C>::setUnderlying
的单个实例必须能够接受任何MyArray<X>*
。 MyArray
和Adapter
是库 class。它不知道类型T
或T2
.
例如,我无法将class MyArrayBase
中的void*
替换为B*
。
灯光标准:-
- 我更喜欢使用虚函数的解决方案。
- 如果没有虚拟成本是最理想的,但我认为这是不可能的。
您可以使用某种包装容器的包装器,通常是:
// 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 因为你需要 setUnderlying
从 MyArray<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