多态(纯抽象)地图关键牺牲类型安全吗?

时间:2011-10-28 15:44:55

标签: c++ polymorphism type-safety

某些上下文:(随意跳过)我有一个处理复杂数据的模块,但只需知道它的一些语义。数据可以被认为是一个数据包:模块应该只推理opaque的有效负载字符串,但它最终会将整个事情传递给需要更多信息的人。但是,它必须......“捆绑”有关某些未知数据包信息的数据包,所以我提出了这个:

struct PacketInfo {
  virtual void operator==(PacketInfo const&) const = 0;
  virtual void operator<(PacketInfo const&) const = 0;
  virtual ~PacketInfo() {}
};

class Processor {
  private:
    template <typename T> struct pless {
      bool operator()(T const* a, T const* b) const {
        assert(a && b);
        return *a < *b;
      }
    };
    // this is where the party takes place:
    std::map<PacketInfo const*,X,pless<PacketInfo> > packets;
  public:
    void addPacket(PacketInfo const*,X const&);
};

现在,我们的想法是,用户实现了他的PacketInfo语义并将其传递给我的班级。例如:
(在回答之前请仔细阅读问题的结尾)

struct CustomInfo : public PacketInfo {
  uint32_t source;
  uint32_t dest;
  void operator==(PacketInfo const& b) const {
    return static_cast<CustomInfo const&>(b).dest == dest
    && static_cast<CustomInfo const&>(b).source == source;
  }
  // operator< analogous
};

在我使用static_cast时,大多数人会使用dynamic_cast,但rtti会停用为项目政策。当然我可以回家酿造我自己的类型信息,之前我已经这样做了,但这不是问题所在。

问题是:如何在不牺牲类型安全的情况下获得我想要的东西(即在不知道其内容的情况下拥有地图密钥),即根本不进行投射?我非常想让Processor类保持非模板类型。

3 个答案:

答案 0 :(得分:2)

你做不到。您可以在编译时知道类型,也可以在运行时检查它们。没有银弹。

答案 1 :(得分:1)

完全一般性的答案应该涉及双重调度。我们的想法是,如果nPacketInfo个不同的子类,则需要n * (n - 1) / 2实现比较运算符。实际上,如果您将CustomInfoAwesomePersonalInfo进行比较会发生什么?这涉及提前了解整个层次结构,示例代码显示在this SO question

如果您确定可以在内部使用相同的类型强制执行地图(因此您确定只需要n运算符实现),那么拥有{没有意义{1}}。只需使用map<PacketInfo, X>

有几种方法可以做到这一点。这里最简单的方法是在数据包类型上模板map<ConcretePacketInfo, X>,如果你想在某处“擦除”模板参数并使用公共代码因素,可能会从Processor类继承。

另一个便宜的解决方案如下:保持代码不变,但使BasicProcessor成为仅定义相关Processor的模板:

addPacket

这可确保调用者操作class BasicProcessor { private: template <typename T> struct pless { bool operator()(T const* a, T const* b) const { assert(a && b); return *a < *b; } }; protected: std::map<PacketInfo const*, X, pless<PacketInfo>> packets; }; // You only need these lines in a public header file. template <typename Packet> class Processor : public BasicProcessor { public: void addPacket(Packet const* p, X const& x) { this->packets[p] = x; } }; 对象并仅添加正确的数据包类型。在我看来,Processor<CustomPacket>类必须是模板类。

此方法的名称为Thin Template Idiom,其中底层实现不是类型安全的(以避免相对于模板的代码膨胀),但是您添加了一层薄层模板以在接口级别恢复类型安全性。

答案 2 :(得分:0)

我看到的最明显的问题是您的operator<operator== 函数不是const。所以你不能通过指针来调用它们 const或对const的引用。他们应该是:

virtual voie operator==(PacketInfo const& other) const = 0;
virtual voie operator<(PacketInfo const& other) const = 0;

另外,从逻辑上讲,如果你定义这两个,你应该定义另一个 四强。我通常会处理这个问题的方法是定义一个多态 成员函数compare,返回值<==> 0, 取决于其this对象是否小于,等于或大于 比其他对象。这样,派生类只有一个 实现的功能。

此外,您肯定需要某种类型的RTTI或双重调度 为了保证两个对象具有相同的类型 比较它们(以及在没有比较时如何订购比较)。