gpt4 book ai didi

c++ - 混合模板与多态性

转载 作者:塔克拉玛干 更新时间:2023-11-03 07:38:56 25 4
gpt4 key购买 nike

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 的重载函数来计算这些值。我可以在返回模板类型的类中编写函数吗?喜欢:

class A
{
<class T> GetVar()
}

此外,我觉得我正在以完全不正确的方式处理这个问题。我可以混合使用模板和继承吗?如果不是,应该如何设计?

最佳答案

您有几个选择。我将首先解释我的首选解决方案。

1。使用动态调度

如果您有一个基类类型的数组,为什么还要用 Var 做些事情?该变量特定于子类。如果你在某处有一个 A,你甚至不应该关心 B 在那个地方有或没有什么。

对类型化变量的操作应该封装在基类的虚函数中。如果您想执行条件和其他操作,也许您可​​以将该条件封装到一个返回 bool 值的虚函数中。

2a。删除基类并使用变体

有时,您事先知道将进入该列表的类型数量。使用变体并删除基类是一个很好的解决方案,可能适用于您的情况。

假设您只有 intdoublestd::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。删除基类并使用泛型函数

完全放弃基类,并模板化对它们进行操作的函数。您可以将所有函数移动到一个接口(interface)或多个 std::function 中。对其进行操作而不是直接对函数进行操作。

这是我的意思的一个例子:

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,它会很快导致代码膨胀。

3。使用访客

您可以创建一个分配给正确类型的访问者。

此解决方案与您排除的解决方案非常接近,但非常麻烦,并且在添加案例时很容易中断。

像这样:

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

4。使用 std::any

这是简单地返回具有删除类型的变量。如果不将其返回,则无法对其进行任何操作:

class A {
std::any GetVar()
};

我个人不喜欢这个解决方案,因为它很容易损坏,而且一点也不通用。在那种情况下我什至不会使用多态性。

关于c++ - 混合模板与多态性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55830885/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com