混合模板与多态性
class A
{
friend void foo();
virtual void print_Var() const{};
};// does not contain variable Var;
template<class T>
class B : public A
{
T Var;
public:
B(T x):Var(x){}
void print_Var() const override
{
std::cout<<Var<<std::endl;
}
};
void foo()
{
std::array<std::unique_ptr<A>, 3> Arr = {
std::make_unique<B<int>>(100),
std::make_unique<B<int>>(20),
std::make_unique<B<std::string>>("Hello Stackoverflow")
};
std::shuffle(Arr.begin(), Arr.end(), std::mt19937(std::random_device()())); // 3rd parameter generated by Clang-Tidy
for (auto &i: Arr)
{
i->print_Var(); // OK
// auto z = i->Var // no member named Var in A
// obviously base class does not contain such variable
// if (i->Var==20) {/* do something*/}
// if (i->Var=="Hello Stackoverflow") {/* do something*/}
}
}
说明:我想遍历指向 A 的指针数组,其中填充了指向从 A 派生的类的指针,并根据变量 Var 的类型,执行一些 if() 语句.问题是我无法访问 Var,导致它不是基类的成员.但是,可以通过例如返回 void 的重载函数来计算这些值.我可以在返回模板类型的类中编写函数吗?喜欢:
Explanation: I want to iterate over array of pointers to A, which is filled with pointers to classes derived from A, and depending on what type is variable Var, do some if( ) statement. Problem is that i cannot access Var, cause its not member of base class. However, it's possible to cout those values by, for example, overloaded function returning void. Could i write function in A class that returns templated type? like:
class A
{
<class T> GetVar()
}
此外,我觉得我在以完全不正确的方式处理这个问题.我可以像这样混合模板和继承吗?如果不是,应该如何设计?
Besides, I feel like I'm dealing with this problem in totally improper way. Can i mix templates and inheritance like that? If not, how should it be designed?
推荐答案
您有几个选择.我将首先解释我的首选解决方案.
You have a few choices. I'll explain my preferred solution first.
如果你有一个基类类型的数组,你为什么还要用 Var
做一些事情?该变量特定于子类.如果你在某个地方有一个 A
,你甚至不应该关心在那个地方有什么或没有什么 B
.
If you have an array of a base class type, why do you even want to do stuff with Var
? That variable is specific to the child class. If you have a A
somewhere, you shouldn't even care what B
has or hasn't at that place.
对类型化变量的操作应该封装在基类中的虚函数中.如果你想做条件和东西,也许你可以把条件封装到一个返回布尔值的虚函数中.
Operations on the typed variable should be encapsulated in virtual function in the base class. If you want to do condition and stuff, maybe you could encapsulate that condition into a virtual function that returns a boolean.
有时,您事先知道将进入该列表的类型数量.使用变体并删除基类是一个很好的解决方案,可能适用于您的情况.
Sometimes, you know in advance the amount of types that will go into that list. Using a variant and drop the base class is a good solution that may apply to your case.
假设你只有 int
、double
和 std::string
:
Let's say you only have int
, double
and std::string
:
using poly = std::variant<B<int>, B<double>, B<std::string>>;
std::array<poly, 3> arr;
arr[0] = B<int>{};
arr[1] = B<double>{};
arr[2] = B<std::string>{};
// arr[2] = B<widget>{}; // error, not in the variant type
std::visit(
[](auto& b) {
using T = std::decay_t<decltype(b)>;
if constexpr (std::is_same_v<B<int>, T>) {
b.Var = 2; // yay!
}
},
arr[0]
);
2b.删除基类并使用泛型函数
完全删除基类,并模板化对它们进行操作的函数.您可以将所有函数移动到一个接口或多个 std::function
中.直接操作那个而不是函数.
2b. Drop the base class and use generic functions
Drop the base class entirely, and template your functions that do operation on them. You can move all your function into an interface or many std::function
. Operate on that instead of the function directly.
这是我的意思的一个例子:
Here's an example of what I meant:
template<typename T>
void useA(T const& a) {
a.Var = 34; // Yay, direct access!
}
struct B {
std::function<void()> useA;
};
void createBWithInt() {
A<int> a;
B b;
b.useA = [a]{
useA(a);
};
};
这适用于您只有很少操作的情况.但是如果你有很多操作或者如果你有很多类型的std::function
,它会很快导致代码膨胀.
This is fine for cases where you only have few operations. But it can quickly lead to code bloat if you have a lot of operations or if you have many types of std::function
.
您可以创建一个分派给正确类型的访问者.
You could create a visitor that dispatch to the right type.
此解决方案与您所不知道的非常接近,但非常麻烦,并且在添加案例时很容易中断.
This solution would be much close to what you except, but is quite combersome and can break easily when adding cases.
像这样:
struct B_Details {
protected:
struct Visitor {
virtual accept(int) = 0;
virtual void accept(double) = 0;
virtual void accept(std::string) = 0;
virtual void accept(some_type) = 0;
};
template<typename T>
struct VisitorImpl : T, Visitor {
void accept(int value) override {
T::operator()(value);
}
void accept(double) override {
T::operator()(value);
}
void accept(std::string) override {
T::operator()(value);
}
void accept(some_type) override {
T::operator()(value);
}
};
};
template<typename T>
struct B : private B_Details {
template<typename F>
void visit(F f) {
dispatch_visitor(VisitorImpl<F>{f});
}
private:
virtual void dispatch_visitor(Visitor const&) = 0;
};
// later
B* b = ...;
b->visit([](auto const& Var) {
// Var is the right type here
});
当然,你必须为每个子类实现dispatch_visitor
.
Then of course, you have to implement the dispatch_visitor
for each child class.
这实际上是返回带有类型擦除的变量.你不能在不回滚的情况下对其进行任何操作:
This is litteraly returning the variable with type erasure. You cannot do any operation on it without casting it back:
class A {
std::any GetVar()
};
我个人不喜欢这个解决方案,因为它很容易损坏并且根本不通用.在那种情况下,我什至不会使用多态.
I personnaly don't like this solution because it can break easily and is not generic at all. I would not even use polymorphism in that case.
相关文章