某些上下文:(随意跳过)我有一个处理复杂数据的模块,但只需知道它的一些语义。数据可以被认为是一个数据包:模块应该只推理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
类保持非模板类型。
答案 0 :(得分:2)
你做不到。您可以在编译时知道类型,也可以在运行时检查它们。没有银弹。
答案 1 :(得分:1)
完全一般性的答案应该涉及双重调度。我们的想法是,如果n
有PacketInfo
个不同的子类,则需要n * (n - 1) / 2
实现比较运算符。实际上,如果您将CustomInfo
与AwesomePersonalInfo
进行比较会发生什么?这涉及提前了解整个层次结构,示例代码显示在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或双重调度 为了保证两个对象具有相同的类型 比较它们(以及在没有比较时如何订购比较)。