【发布时间】:2017-04-21 06:21:06
【问题描述】:
我正在尝试创建自定义集合 MyArray<T> 的适配器。
为简单起见,适配器Adapter 只做一件事:转换MyArray<T>::get 的返回结果。
(在实际情况下,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)
目标: 将模板参数的数量从 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是库类。它对T或T2类型一无所知。
例如,我不能将class MyArrayBase中的void*替换为B*。
光线标准:-
- 我更喜欢使用虚函数的解决方案。
- 如果没有虚拟成本,那将是理想的,但我认为这是不可能的。
【问题讨论】:
-
一种方法可能是将
MyArray中的T* get更改为void*,以便实际使用get。 -
@Winestone 这似乎是一个有趣的想法。我从来没有想过。我可能会尽量避免
void*。谢谢。 -
虽然不起作用,但只能消除警告。
-
@Winestone 同意,
static_cast也可以。顺便说一句,这是不雅的。我必须告诉用户一个奇怪的要求(元素必须派生自某个类 -Dummy)。这会降低库的质量/可用性(至少有一点)。 -
cs的构造函数中将bs传递给cs可以吗?然后您可以使用模板来推断第二个模板参数。您也可以拥有Adapter<T2>,然后让AdapterImpl<T2, T>继承它并实现从T转换为T2的虚函数,然后声明Adapter<C>* cs = new AdapterImpl<C, B>();。
标签: c++ templates data-structures c++14 virtual-functions