编译器不接受这个的原因是标准告诉它不要。
标准告诉它不要这样做的原因是,即使非 const 类型不相关,委员会也没有引入 const MyTemplate<Derived*> 是与 const MyTemplate<Base*> 相关类型的规则。他们当然不想要 std::set 的特殊规则,因为通常该语言不会为库类制定特殊情况。
标准委员会不想让这些类型相关的原因是 MyTemplate 可能没有容器的语义。考虑:
template <typename T>
struct MyTemplate {
T *ptr;
};
template<>
struct MyTemplate<Derived*> {
int a;
void foo();
};
template<>
struct MyTemplate<Base*> {
std::set<double> b;
void bar();
};
那么将const MyTemplate<Derived*> 作为const MyTemplate<Base*> 传递是什么意思?这两个类没有共同的成员函数,也不兼容布局。您需要两者之间的转换运算符,否则编译器将不知道无论它们是否为 const 该怎么做。但是标准中定义模板的方式,即使没有模板特化,编译器也不知道该怎么做。
std::set 本身可以提供一个转换运算符,但这只需要制作一个副本(*),您可以自己轻松完成。如果有 std::immutable_set 这样的东西,那么我认为有可能实现这样一个 std::immutable_set<Base*> 可以通过指向相同的 pImpl 从 std::immutable_set<Derived*> 构造。即便如此,如果您在派生类中重载了非虚拟运算符,则会发生奇怪的事情 - 基础容器将调用基础版本,因此如果它有一个非默认比较器可以执行任何操作,则转换可能会取消对集合的排序对象本身而不是它们的地址。因此,转换将伴随着严重的警告。但不管怎样,没有immutable_set,而且 const 和 immutable 不是一回事。
另外,假设Derived 通过虚拟或多重继承与Base 相关。那么您不能仅仅将Derived 的地址重新解释为Base 的地址:在大多数实现中,隐式转换会更改地址。因此,您不能在不复制结构的情况下将包含Derived* 的结构批量转换为包含Base* 的结构。但 C++ 标准实际上允许任何非 POD 类发生这种情况,而不仅仅是多重继承。而Derived 是非 POD,因为它有一个基类。因此,为了支持对std::set 的这种更改,必须更改继承和结构布局的基础。这是 C++ 语言的一个基本限制,标准容器不能以您想要的方式重新解释,而且我不知道有任何技巧可以在不降低效率或可移植性或两者兼而有之的情况下使它们如此。令人沮丧,但这东西很难。
由于您的代码无论如何都按值传递了一个集合,因此您可以制作该副本:
std::set<Derived*> objs;
register_objects(std::set<Base*>(objs.begin(), objs.end());
[编辑:您已将代码示例更改为不按值传递。我的代码仍然有效,afaik 是你能做的最好的事情,除了重构调用代码以首先使用std::set<Base*>。]
为std::set<Base*> 编写一个包装器以确保所有元素都是Derived*,这是Java 泛型的工作方式,比安排您想要高效的转换更容易。所以你可以这样做:
template<typename T, typename U>
struct MySetWrapper {
// Requirement: std::less is consistent. The default probably is,
// but for all we know there are specializations which aren't.
// User beware.
std::set<T> content;
void insert(U value) { content.insert(value); }
// might need a lot more methods, and for the above to return the right
// type, depending how else objs is used.
};
MySetWrapper<Base*,Derived*> objs;
// insert lots of values
register_objects(objs.content);
(*) 实际上,我猜它可以在写入时复制,在以典型方式使用 const 参数的情况下,这意味着它永远不需要进行复制。但是写时复制在 STL 实现中有点名誉扫地,即使我不怀疑委员会会想要强制执行如此重量级的实现细节。