Pet*_*mit 2 c++ inheritance stl
在此之前被标记为重复,我知道这个问题,但在我的情况下,我们讨论的是const容器.
我有2个班:
class Base { };
class Derived : public Base { };
Run Code Online (Sandbox Code Playgroud)
功能:
void register_objects(const std::set<Base*> &objects) {}
Run Code Online (Sandbox Code Playgroud)
我想调用这个函数:
std::set<Derived*> objs;
register_objects(objs);
Run Code Online (Sandbox Code Playgroud)
编译器不接受这个.为什么不?该集合不可修改,因此不存在将非派生对象插入其中的风险.我怎样才能以最好的方式做到这一点?
编辑:
据我所知,现在的编译器的工作方式是set<Base*>和set<Derived*>是完全无关的,并为此函数签名是找不到的.我现在的问题是:编译器为什么这样工作?是否有任何反对意见不认为const set<Derived*>是衍生品const set<Base*>
Ste*_*sop 13
编译器不接受这个的原因是标准告诉它不要这样做.
标准告诉它不要的原因是,即使非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();
};
Run Code Online (Sandbox Code Playgroud)
那么这是什么意思,甚至传递一个const MyTemplate<Derived*>作为const MyTemplate<Base*>?这两个类没有共同的成员函数,并且不是布局兼容的.你需要在两者之间使用转换运算符,否则编译器不知道该怎么做它们是不是const.但是标准中定义了模板的方式,即使没有模板特化,编译器也不知道该做什么.
std::set本身可以提供一个转换操作符,但这只需要复制(*),你可以很容易地做到这一点.如果存在a这样的东西std::immutable_set,那么我认为可以实现这样的,即a std::immutable_set<Base*>可以std::immutable_set<Derived*>通过指向同一个pImpl来构造.即便如此,如果您在派生类中重载了非虚拟运算符,也会发生奇怪的事情 - 基本容器会调用基本版本,因此如果转换具有非默认比较器,则转换可能会对集合进行降序对象本身而不是他们的地址.所以转换会带来沉重的警告.但无论如何,没有一个immutable_set,而const与不可变的不一样.
另外,假设它Derived与Base虚拟或多重继承有关.然后你不能只重新解释a的地址作为a Derived的地址Base:在大多数实现中,隐式转换会改变地址.因此,您不能仅仅批量转换Derived*包含Base*不包含复制结构的结构的结构.但是C++标准实际上允许任何非POD类发生这种情况,而不仅仅是多重继承.并且Derived是非POD,因为它有一个基类.因此,为了支持这种改变std::set,必须改变继承和结构布局的基础.这是C++语言的一个基本限制,标准容器无法以您想要的方式重新解释,而且我不知道任何可以在不降低效率或可移植性的情况下使其成为可能的技巧.这令人沮丧,但这件事很难.
由于您的代码无论如何都是按值传递,您只需制作该副本即可:
std::set<Derived*> objs;
register_objects(std::set<Base*>(objs.begin(), objs.end());
Run Code Online (Sandbox Code Playgroud)
[编辑:您已更改代码示例而不是按值传递.我的代码仍然有效,除了重构调用代码以便首先使用a之外,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);
Run Code Online (Sandbox Code Playgroud)
(*)实际上,我猜它可以写入时复制,在典型方式中使用const参数的情况下,它意味着它永远不需要复制.但是在STL实现中,copy-on-write有点不可信,即使不是我怀疑委员会是否想要强制执行这样一个重量级的实现细节.
| 归档时间: |
|
| 查看次数: |
365 次 |
| 最近记录: |