【问题标题】:mixing templates with polymorphism将模板与多态性混合
【发布时间】:2019-09-13 19:12:01
【问题描述】:
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()
}

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

【问题讨论】:

  • 你可以使用CRTP在基类中做一个getter。
  • @NathanOliver 您不能拥有 CRTP 基类的异构集合。
  • @GuillaumeRacicot 对。我是新人,我错过了一些东西。
  • cdn.fbsbx.com/v/t59.2708-21/… 这是我想解决的问题,我想遍历 12 个框,如果遇到“Game Over”框,我想退出函数。但可能我这样做完全错误。
  • 这真的是模板问题,还是设计问题?如果不使用模板,您也会遇到同样的情况。

标签: c++ templates polymorphism


【解决方案1】:

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

1。使用动态调度

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

对类型变量的操作应该封装在基类的虚函数中。如果你想做条件和东西,也许你可以将该条件封装到一个返回布尔值的虚函数中。

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。删除基类并使用泛型函数

完全放弃基类,并将对它们进行操作的函数模板化。您可以将所有功能移动到一个界面或多个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()
};

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

【讨论】:

    【解决方案2】:

    我认为这将是最简单的方法。只需将比较方法移动到接口并在派生类中覆盖它。将以下行添加到您的示例中:

    class A
    {
       /*..................................................*/
       virtual bool comp(const int) const { return false; }
       virtual bool comp(const std::string) const { return false; }
       virtual bool comp(const double) const { return false; }  
    };
    
    template<class T>
    class B : public A
    {
       /*..................................................*/
       virtual bool comp(const T othr) const override { return othr == Var; }
    };
    
    void foo()
    {
          /*..................................................*/
          if (i->comp(20))
          {
             /* do something*/
          }
    
          if (i->comp("Hello Stackoverflow"))
          {
             /* do something*/
          }
          /*..................................................*/
    }
    

    【讨论】:

      猜你喜欢
      • 2011-12-19
      • 1970-01-01
      • 1970-01-01
      • 2011-09-20
      • 1970-01-01
      • 2019-09-24
      相关资源
      最近更新 更多