混合具有多态性的模板 [英] mixing templates with polymorphism

查看:80
本文介绍了混合具有多态性的模板的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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.

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

完全丢弃基类,并对在其上执行操作的函数进行模板化.您可以将所有功能移至一个接口或多个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.

这是我的意思的示例:

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.

这篇关于混合具有多态性的模板的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆