Why is it impossible to pass the <Derived *> const set as the const specified by the <Base *> function for the function?
Before this is marked as a duplicate, I know this question, but in my case we are talking about const containers.
I have 2 classes:
class Base { }; class Derived : public Base { }; And function:
void register_objects(const std::set<Base*> &objects) {} I would like to call this function as:
std::set<Derived*> objs; register_objects(objs); The compiler does not accept this. Why not? The set is not modified, so there is no risk that non-derived objects will be inserted into it. How can I do this in the best way?
Edit:
I understand that now the compiler works in such a way that set<Base*> and set<Derived*> are not completely connected, and therefore the function signature was not found. Now my question is: why does the compiler work like this? Will there be objections not to see const set<Derived*> as a derivative of const set<Base*>
The reason the compiler does not agree with this is because the standard does not report this.
The reason the standard says that this is not so is because the committee did not introduce, in order to introduce a rule, that const MyTemplate<Derived*> is a bound type to const MyTemplate<Base*> , even if non-context types are not bound. And they, of course, did not want a special rule for std :: set, since in general the language does not make special cases for library classes.
The reason the standards committee did not want to bind these types is because MyTemplate might not have container semantics. 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(); }; Then what does it even mean to pass const MyTemplate<Derived*> as const MyTemplate<Base*> ? These two classes do not have common member functions and are not compatible with layouts. You will need a conversion operator between the two, or the compiler had no idea what to do, regardless of whether they are or not. But the way the templates are defined in the standard, the compiler has no idea what to do even without specialized templates.
std::set itself could provide the conversion operator, but it just needs to make a copy (*), which you can do quite easily. If there was such a thing as std::immutable_set , then, I think, it would be possible to implement such that a std::immutable_set<Base*> could be built from std::immutable_set<Derived*> , simply by pointing to the same pImpl. However, strange things will happen if you have non-virtual operators in the derived class being overloaded - the base container will call the base version, so the transformation can de-order the set if it had a non-standard comparator that did something with the objects themselves, not their addresses. Thus, the appeal will be accompanied by serious reservations. But in any case, there is no immutable_set , and const is not the same as immutable_set.
Additionally, suppose Derived is associated with Base with virtual or multiple inheritance. Then you cannot just rethink the Derived address as the Base address: in most implementations, an implicit conversion changes the address. It follows that you cannot simply batch convert a structure containing Derived* to a structure containing Base* without copying the structure. But the C ++ standard actually allows this to happen for any class other than POD, and not just with multiple inheritance. And Derived is a non-POD, since it has a base class. Therefore, to support this change to std::set , the basic principles of inheritance and the layout of the structure must be changed. This is the main limitation of the C ++ language that standard containers cannot be re-interpreted the way you want, and I donโt know any tricks that could do them without compromising efficiency or portability, or both. It is frustrating, but it is difficult.
Since your code passes value by value anyway, you can simply make this copy:
std::set<Derived*> objs; register_objects(std::set<Base*>(objs.begin(), objs.end()); [Edit: you changed your sample code so as not to jump by value. My code still works, and afaik is the best you can do, besides refactoring the calling code, to use std::set<Base*> in the first place.]
Writing a wrapper for std::set<Base*> , which ensures that all Derived* elements, a way to work with Java generics, is easier than organizing the transformation you want to be efficient. So you can 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); (*) In fact, I think he could copy-to-write, which in the case of the const parameter used in the usual way, would mean that he never needs to make a copy. But copy-by-file copying is a little discredited in STL implementations, and even if I had no doubt that the committee would want to impose such a detail on a heavy implementation.
If your register_objects function receives an argument, it can place / expect any subclass of Base . This is what he signs.
This is a violation of the principle of Liskovโs signature.
This particular problem is also called Covariance . In this case, when the function argument is a constant container, it can be made to work. If the argument container is modified, it cannot work.
Take a look here first: Is the array derived the same as the base array . In your case, the set of derivatives is a completely different container from the set of bases, and since there is no implicit conversion operator available to convert between them, the compiler reports an error.
Firstly, it seems a little strange that you are not following the link ...
Secondly, as mentioned in another post, you would be better off creating the passed set as std :: set <Base *>, and then introduce the Derived class for each set element.
Your problem, of course, arises from the fact that the 2 types are completely different. std :: set <Derived *> is in no way inherited from std :: set <Base *> in relation to the compiler. These are just two different types of kit ...
Well, as stated in the question you mentioned, set<Base*> and set<Derived*> are different objects. The register_objects () function accepts a set<Base*> object. Therefore, the compiler is not aware of any register_objects () that accept set<Derived*> . The parameter constant does not change anything. The solutions indicated in the question quoted seem to be the best you can do. Depends on what you need to do ...
As you know, these two classes are very similar as soon as you delete non-constant operations. However, in C ++, inheritance is a type property, while const is a simple classifier on top of types. This means that you cannot correctly state that const X comes from const Y , even if X comes from Y.
In addition, if X is not inherited from Y, this applies to all cv-qualifying variants of X and Y. This applies to std::set instances. Since std::set<Foo> does not inherit from std::set<bar> , std::set<Foo> const does not inherit from std::set<bar> const .
You are absolutely right that this is logically permissible, but this will require additional language functions. They are available in C # 4.0 if you are interested in doing this in another language. See here: http://community.bartdesmet.net/blogs/bart/archive/2009/04/13/c-4-0-feature-focus-part-4-generic-co-and-contra-variance- for-delegate-and-interface-types.aspx
I have not yet seen that it is connected, so here in the C ++ FAQ Lite field the marker value is indicated:
http://www.parashift.com/c++-faq-lite/proper-inheritance.html#faq-21.3
I think that their coincidence with the bags! = Bag-of-Fruit approaches this question.