【问题标题】:Providing possible parameters of different types in a statically typed language在静态类型语言中提供不同类型的可能参数
【发布时间】:2020-04-07 16:39:48
【问题描述】:

比如说,我有 class Action,它存储了一些 Guys 并在某个时间将它们带入行动:

class Action {
  std::vector<Guy> guys;

  void bring() {
    Guy newcomer = guys.back();
    switch (newcomer.characteristic) {
      // see below
    }
  }

  // ...
};

switch 的问题是:一个人可能有一个特质,可能需要进一步解释,也可能不需要。例如:

case loser: // no more information needed to continue
  banish(guy);
  break;
case buddy:
  Guy buddy = newcomer.whose(); // the "explanation" which is needed for this case
  if (is_here(buddy)) {
    newcomer.happiness += 42;
  }

我怎样才能合理地存储可能需要的信息?不幸的是,它可能有不同的类型(就编程语言而言),因此为每个可能的枚举器提供类成员似乎不是一个很好的扩展选项,并且由于容易进行不适当的调用而容易出错:

enum Characteristic { // isn't "enum class" for brevity of the example
  buddy, gourmet, loser, // etc.
};
class Guy {
 public:
  Characteristic characteristic;

  Guy whose() {
    if (characteristic != buddy) {
      throw std::logic_error("inappropriate call"); // awful!
    }
    return this->buddy;
  }
  Food favorite() { // for "gourmet"
    // same as above?!
  }
  // etc.
};

我曾考虑过继承,但没有得到任何令人满意的结果。最好的主意似乎是

void Derived_guy::be_brought(Action& action) {
  // modify action in any way
}

但是,Guy 需要知道 Action 是高级抽象 - 糟糕的设计?

我还能如何解决我的问题?

【问题讨论】:

    标签: c++ oop parameters


    【解决方案1】:

    您可以将枚举替换为可以存储任何信息的std::variant

    struct Buddy
    {
        Guy whose;
    };
    
    struct Loser{};
    
    struct Gourmet
    {
        Food favorite;
    };
    
    // ...
    
    using Characteristic = std::variant<Buddy, Loser, Gourmet>;
    
    class Guy {
    public:
        Characteristic characteristic;
    };
    

    最后:

    struct MyVisitor
    {
        Guy& newcomer;
    
        void operator()(Loser) const { banish(newcomer); }
        void operator()(Buddy& buddy) const
        {
            if (is_here(buddy.whose())) {
                newcomer.happiness += 42;
            }
        }
        // ...
    };
    
    void bring() {
        Guy newcomer = guys.back();
    
        std::visit(MyVisitor{newcomer}, newcomer.characteristic);
    }
    

    【讨论】:

    • 感谢您的好主意!你为什么在这里使用访问者而不是通常的 switch-case?
    • 派发变体的常用方法是std::visit,您可能会看到有"in-place" 访问者的方法。
    猜你喜欢
    • 2011-10-29
    • 1970-01-01
    • 2016-09-26
    • 1970-01-01
    • 2011-02-11
    • 1970-01-01
    • 2011-02-09
    相关资源
    最近更新 更多