如何在复合设计模式中动态选择operator []的返回类型?

问题描述 投票:-1回答:1

首先,我想指出这是我第一次使用动态多态和复合设计模式。

我想使用复合设计模式来创建一个class Tree,它能够采用Tree类型的不同对象,一种复合类型,或Leaf,一种原子类型。 Tree和Leaf都继承自一个普通的类Nature。树可以将叶子或树对象存储到std::vector<std::shared_ptr<Nature>> children中。我想用这种语法填充矢量children(所以我想我必须使用variadic,考虑输入列表中的通用输入数量),如下所示:

Leaf l0(0);
Leaf l1(1);
Tree t0;
Tree t1;
t0.add(l0,l1);
t1.add(t0,l0,l1); // or in general t1.add(t_00,...,t_0n, l_00,...,l_0n,t10,...,t1n,l10,...,l1n,.... )

然后我还将通过Tree访问operator[ ]的不同元素。所以例如t1[0]返回t0t1[0][0]返回l0,而t1[0][1]返回l0

我也想要一个同质的行为。因此,要么使用->或点来访问所有级别(树或叶子)上的方法。

有可能实现这种行为吗?

这些类的实现可以如下所示:

class Nature
{
  public:
    virtual void nature_method() = 0;
    virtual~Nature();
    //virtual Nature& operator[] (int x);

};
class Leaf: public Nature
{
    int value;
  public:
    Leaf(int val)
    {
        value = val;
    }
    void nature_method() override
    {
        std::cout << " Leaf=="<<value<<" ";
    }
}; 
class Tree: public Nature
{ 
    private:
    std::vector <std::shared_ptr< Nature > > children;
    int value;

    public:
    Tree(int val)
    {
        value = val;
    }


     void add(const Nature&);

     void add(const Leaf& c)
    {
        children.push_back(std::make_shared<Leaf>(c));
    } 

     void add(const Tree& c)
    {
        children.push_back(std::make_shared<Tree>(c));
    }   


    void add(std::shared_ptr<Nature> c)
    {
        children.push_back(c);
    }

     template<typename...Args>
    typename std::enable_if<0==sizeof...(Args), void>::type
    add(const Leaf& t,Args...more)
    {
     children.push_back(std::make_shared<Leaf>(t));
    };

    template<typename...Args>
    typename std::enable_if<0==sizeof...(Args), void>::type
    add(const Tree& t,Args...more)
    {
     children.push_back(std::make_shared<Tree>(t));
    };


    template<typename...Args>
    typename std::enable_if<0<sizeof...(Args), void>::type
    add(const Leaf& t,Args...more)
    {
      children.push_back(std::make_shared<Leaf>(t));
      add(more...);
    };

    template<typename...Args>
    typename std::enable_if<0<sizeof...(Args), void>::type
    add(const Tree& t,Args...more)
    {
      children.push_back(std::make_shared<Tree>(t));
      add(more...);
    };

    void nature_method() override
    {
        std::cout << " Tree=="<< value;
        for (int i = 0; i < children.size(); i++)
          children[i]->nature_method();
    }
}

我可以实现重载operator []来返回指向Nature或Nature对象的指针,如下所示:

 Nature& operator[] (int x) {
        return *children[x];
    }

 std::shared_ptr< Nature > operator[] (int x) {
        return children[x];
    }

在这两种情况下,返回类型都与Nature相关。这是因为它可能是一个LeafTree,这是事先不知道的。但由于必须在编译时知道运算符的返回类型,我不能做其他事情。

但是,如果返回的类型与Tree相关,我不能再使用operator [],因为我已经强制它为Nature

如何动态选择Tree的返回类型Leaf[]?这有什么解决方法吗?

我可以认为operator []是Nature类中的一个虚拟方法,但我仍然不知道如何解决这个问题。

我也读过有关协变类型的内容,但我不知道它们是否适用于此。

谢谢。

c++11 design-patterns dynamic polymorphism composite
1个回答
0
投票

如果您想要类型安全,则必须在每个使用站点检查[]的返回值,以确定它是Tree还是Leaf

你也可以选择不是类型安全的,并且如果你以一种应该是Leaf的方式使用Tree,则调用未定义的行为。

而不管:

virtual Nature& operator[](std::ptrdiff_t i) {
  throw std::invalid_argument("Not a Tree");
}
virtual Nature const& operator[](std::ptrdiff_t i) const {
  throw std::invalid_argument("Not a Tree");
}

Nature,接着是:

virtual Nature& operator[](std::ptrdiff_t i) final override {
  auto r = children.at((std::size_t)x);
  if (r) return *r;
  throw std::out_of_range("no element there");
}
virtual Nature const& operator[](std::ptrdiff_t i) const final override {
  auto r = children.at((std::size_t)x);
  if (r) return *r;
  throw std::out_of_range("no element there");
}

Tree

当你在错误的类型上使用[]时,这会产生异常。

© www.soinside.com 2019 - 2024. All rights reserved.