我有一个"包装"模板看起来像这样:
template <typename T>
class Wrapper {
virtual T* get() = 0;
}
然后我有以下继承方案:
class A {
// things
}
class B : public A {
// more things
}
我有另一个使用该包装器的类:
class Worker {
void work(Wrapper<A>* wa){
A* a = wa->get();
//do stuff with a.
}
}
可以使用A*
完成所有操作{}可以使用B*
,但我无法将Wrapper<B>*
作为Wrapper<A>*
传递给工作人员。有没有办法允许这个?
我已经阅读了有关C ++概念的内容,但我不知道如何解决这个问题。
答案 0 :(得分:3)
如果每个班级报告其基数:
class A {
using Base = void;
// things
}
class B : public A {
using Base = A;
// more things
}
你可以做到
template<typename T>
class Wrapper : public EmptyForVoid<T::Base>::Type
使用EmptyForVoid
进行基类选择:
template<typename T>
struct EmptyForVoid { using Type = T; };
template<>
struct EmptyForVoid<void> { struct Type {}; };
这将使Wrapper
遵循与它们包装的类型相同的继承树。
答案 1 :(得分:-1)
你不能直接这样做,因为C ++没有协方差。在C ++中,默认情况下方法不是虚拟的(与Java不同,我猜C#),目前尚不清楚这是否是一个很棒的想法(语言已经非常复杂)。基本上你要做的就是在work
上有一个很薄的模板包装器:
class Worker {
public:
template <class T>
void work(Wrapper<T>* wa){
work_on_a(wa->get());
}
private:
void work_on_a(A* a) { // do stuff with a}
}
这仍然是安全的,因为只有在包装类型派生自A或以其他方式定义隐式转换为A
时才会编译,否则对work_on_a
的函数调用将失败。
你可以在这里做各种优点和缺点的更复杂的事情,但这使代码尽可能简单,并最大限度地减少模板膨胀。
虽然注意,如果work
也需要是虚拟的,这将不起作用(因为虚函数不能是模板),并且事情会变得很快。
另一种方法是争辩说包装器根本不应该传递给work
;如果所有你需要做的工作都是指向A
的指针,那就是你应该传入的所有内容。但是根据你的真实代码可能不是一个选项,或者没有意义。
答案 2 :(得分:-1)
Curiously Recurring Template Pattern的一点点:
class SubBase {
virtual T* get() { /* default impl. */ }
};
template <typename T>
class Base : public SubBase {};
template <typename T>
class Wrapper : public Base<T> {
virtual T* get() = 0;
}
...我不知道你的继承图是什么样的,但只要你的包裹的T
类型没有get()
方法,这应该可行。
<小时/> 编辑:这个简单的片段不起作用,也不是真正的CRTP,正如已经指出的那样;我只能在fairly significant modification之后做一个这种排序工作的例子。