为什么不可能传递const集合< Derived *> as const set< Base *>到一个函数? [英] Why is is it not possible to pass a const set<Derived*> as const set<Base*> to a function?

查看:174
本文介绍了为什么不可能传递const集合< Derived *> as const set< Base *>到一个函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在此标记为重复之前,我注意到

Before this is marked as duplicate, I'm aware of this question, but in my case we are talking about const containers.

我有两个类:

class Base { };
class Derived : public Base { };

还有一个功能:

void register_objects(const std::set<Base*> &objects) {}

我想调用这个函数:

std::set<Derived*> objs;
register_objects(objs);

编译器不接受此。为什么不?该集合不可修改,因此不会有插入非派生对象的风险。我如何以最好的方式做这个?

The compiler does not accept this. Why not? The set is not modifiable so there is no risk of non-Derived objects being inserted into it. How can I do this in the best way?

编辑

我知道现在编译器工作在 set< Base *> set< Derived *> 的方式是完全不相关的,未找到。我现在的问题是:为什么编译器这样工作?有没有任何反对看不到 const set< Derived *> 作为的派生的const set< Base *>


I understand that now the compiler works in a way that set<Base*> and set<Derived*> are totally unrelated and therefor the function signature is not found. My question now however is: why does the compiler work like this? Would there be any objections to not see const set<Derived*> as derivative of const set<Base*>

推荐答案

编译器不接受这一点的原因是,标准告诉它不要。

The reason the compiler doesn't accept this is that the standard tells it not to.

标准告诉它的原因是委员会没有引入一个规则 const MyTemplate< Derived *> const MyTemplate< Base *> 的相关类型,即使非const类型不相关。他们肯定不想要一个std :: set的特殊规则,因为一般来说,语言不会为库类创建特殊的例子。

The reason the standard tells it not to, is that the committee did not what to introduce a rule that const MyTemplate<Derived*> is a related type to const MyTemplate<Base*> even though the non-const types are not related. And they certainly didn't want a special rule for std::set, since in general the language does not make special cases for library classes.

标准委员会不想让这些类型相关,是MyTemplate可能没有容器的语义。考虑:

The reason the standards committee didn't want to make those types related, is that MyTemplate might not have the semantics of a container. Consider:

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。但是在标准中定义模板的方式,即使没有模板特殊化,编译器也不知道该怎么做。

Then what does it even mean to pass a const MyTemplate<Derived*> as a const MyTemplate<Base*>? The two classes have no member functions in common, and aren't layout-compatible. You'd need a conversion operator between the two, or the compiler would have no idea what to do whether they're const or not. But the way templates are defined in the standard, the compiler has no idea what to do even without the template specializations.

std :: set 本身可以提供一个转换操作符,但这只需要一个副本(*),你可以自己做足够容易。如果有这样的事情,如 std :: immutable_set ,那么我认为有可能实现这样一个 std :: immutable_set<仅通过指向相同的pImpl就可以从 std :: immutable_set< Derived *> 构造基本*> 即使这样,奇怪的事情会发生,如果你有非虚拟运算符重载在派生类 - 基本容器将调用基本版本,因此,如果它有一个非默认比较器,对象本身而不是他们的地址。所以转换会伴随着重要的警告。但是无论如何,没有 immutable_set ,并且const不是不变的。

std::set itself could provide a conversion operator, but that would just have to make a copy(*), which you can do yourself easily enough. If there were such a thing as a std::immutable_set, then I think it would be possible to implement that such that a std::immutable_set<Base*> could be constructed from a std::immutable_set<Derived*> just by pointing to the same pImpl. Even so, strange things would happen if you had non-virtual operators overloaded in the derived class - the base container would call the base version, so the conversion might de-order the set if it had a non-default comparator that did anything with the objects themselves instead of their addresses. So the conversion would come with heavy caveats. But anyway, there isn't an immutable_set, and const is not the same thing as immutable.

假设 Derived 与通过虚拟或多重继承的 Base 相关。然后你不能只是重新解释 Derived 的地址作为 Base 的地址:在大多数实现中,隐式转换改变地址。因此,您不能只将包含 Derived * 的结构批量转换为包含 Base * 结构。但是C ++标准实际上允许这种情况发生在任何非POD类,而不仅仅是多重继承。和 Derived 是非POD,因为它有一个基类。因此,为了支持对 std :: set 的更改,继承和结构布局的基本原则必须更改。这是C ++语言的一个基本限制,标准容器不能以你想要的方式重新解释,我不知道任何技巧,可以使它们如此不降低效率或可移植性或两者。

Also, suppose that Derived is related to Base by virtual or multiple inheritance. Then you can't just reinterpret the address of a Derived as the address of a Base: in most implementations the implicit conversion changes the address. It follows that you can't just batch-convert a structure containing Derived* as a structure containing Base* without copying the structure. But the C++ standard actually allows this to happen for any non-POD class, not just with multiple inheritance. And Derived is non-POD, since it has a base class. So in order to support this change to std::set, the fundamentals of inheritance and struct layout would have to be altered. It's a basic limitation of the C++ language that standard containers cannot be re-interpreted in the way you want, and I'm not aware of any tricks that could make them so without reducing efficiency or portability or both. It's frustrating, but this stuff is difficult.

因为你的代码通过值传递一个值,你可以只是复制:

Since your code is passing a set by value anyway, you could just make that copy:

std::set<Derived*> objs;
register_objects(std::set<Base*>(objs.begin(), objs.end());

std :: set< Base *> 确保所有元素 Derived * ,Java方法的工作方式比安排转换所以你可以这样做:

Writing a wrapper for std::set<Base*> that ensures all elements are Derived*, the way Java generics work, is easier than arranging for the conversion you want to be efficient. So you could do something like:

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);

(*)其实,我想它可以copy-on-write, const参数使用在典型的方式意味着它从来不需要做副本。但是写时拷贝在STL实现中有点不可信,即使不是我怀疑委员会会要求这样一个重量级的实现细节。

(*) Actually, I guess it could copy-on-write, which in the case of a const parameter used in the typical way would mean it never needs to do the copy. But copy-on-write is a bit discredited within STL implementations, and even if it wasn't I doubt the committee would want to mandate such a heavyweight implementation detail.

这篇关于为什么不可能传递const集合&lt; Derived *&gt; as const set&lt; Base *&gt;到一个函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆