How to generate a compiler warning/error when object sliced - c++

I want to know if it is possible to let compiler issue a warning/error for code as following:
Note:
1. Yea, it is bad programming style and we should avoid such cases - but we are dealing with legacy code and hope compiler can help identify such cases for us.)
2. I prefer a compiler option (VC++) to disable or enable object slicing, if there is any.
class Base{};
class Derived: public Base{};
void Func(Base)
{
}
//void Func(Derived)
//{
//
//}
//main
Func(Derived());
Here if I comment out the second function, the first function would be called - and the compiler (both VC++ and Gcc) feels comfortable with that.
Is it C++ standard? and can I ask compiler (VC++) to give me a warning when met such code?
Thanks so much!!!
Edit:
Thanks all so much for your help!
I can't find a compiler option to give a error/warning - I even posted this in MSDN forum for VC++ compiler consultant with no answer. So I am afraid neither gcc nor vc++ implemented this feature.
So add constructor which take derived classes as paramter would be the best solution for now.
Edit
I have submit a feedbak to MS and hope they will fix it soon:
https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=421579
-Baiyan

If you can modify the base class you could do something like:
class Base
{
public:
// not implemented will cause a link error
Base(const Derived &d);
const Base &operator=(const Derived &rhs);
};
Depending on your compiler that should get you the translation unit, and maybe the function where the slicing is happening.

As a variation of Andrew Khosravian's answer, I'll suggest using templated copy constructors and assignment operators. This way you don't need to know all the derived classes of a given base class in order to safeguard that base class against slicing:
class Base
{
private: // To force a compile error for non-friends (thanks bk1e)
// Not implemented, so will cause a link error for friends
template<typename T> Base(T const& d);
template<typename T> Base const& operator=(T const& rhs);
public:
// You now need to provide a copy ctor and assignment operator for Base
Base(Base const& d) { /* Initialise *this from d */ }
Base const& operator=(Base const& rhs) { /* Copy d to *this */ }
};
Although this reduces the amount of work needed, with this approach you still need to mess with each base class in order to safeguard it. Also, it will cause problems if there are legitimate conversions from Base to SomeOtherClass that employ an operator Base() member of SomeOtherClass. (In that case, a more elaborate solution involving boost::disable_if<is_same<T, SomeOtherClass> > can be used.) In any case, you should remove this code once you've identified all instances of object slicing.
To the compiler implementors of the world: Testing for object slicing is definitely something that would be worthwhile creating (optional) warnings for! I can't think of one instance where it would be desired behaviour, and it's very commonly seen in newbie C++ code.
[EDIT 27/3/2015:] As pointed out by Matt McNab, you actually don't need to declare the copy constructor and assignment operator explicitly as I've done above, as they will still be implicitly declared by the compiler. In the 2003 C++ standard, this is explicitly mentioned in footnote 106 under 12.8/2:
Because a template constructor is never a copy constructor, the presence of such a template does not suppress the implicit declaration of a copy constructor. Template constructors participate in overload resolution with other constructors, including copy constructors, and a template constructor may be used to copy an object if it provides a better match than other constructors.

I would suggest adding a constructor to your base class which takes a const reference to the derived class explicitly (with a forward declaration). In my simple test app, this constructor gets called in the slicing case. You could then at least get a run-time assertion, and you could probably get a compile-time assertion with clever use of templates (eg: instantiate a template in a way which generates a compile-time assertion in that constructor). There may also be compiler-specific ways to get compile time warnings or errors when you call explicit functions; for example, you can use "__declspec(deprecated)" for the "slice constructor" in Visual Studio to get a compile-time warning, at least in the function-call case.
So in your example, the code would look like this (for Visual Studio):
class Base { ...
__declspec(deprecated) Base( const Derived& oOther )
{
// Static assert here if possible...
}
...
This works in my test (compile-time warning). Note that it doesn't solve the copy case, but a similarly-constructed assignment operator should do the trick there.
Hope this helps. :)

The best way to combat this problem is usually to follow Scott Meyer's recommendation (see Effective C++) of only having concrete classes at the leaf nodes of your inheritance tree and ensuring that non-leaf classes are abstract by having at least one pure virtual function (the destructor, if nothing else).
It is surprising how often this approach helps clarify the design in other ways, as well. The effort of isolating a common abstract interface is usually a worthwhile design effort in any case.
Edit
Although I originally didn't make this clear, my answer comes from the fact that it is not possible to warn accurately about object slicing at compile time and for this reason it can lead to a false sense of security if you have a compile time assertion, or a compiler warning enabled. If you need to find out about instances of object slicing and need to correct them then it implies that you have the desire and ability to change the legacy code. If this is the case, then I believe that you should seriously consider refactoring the class hierarchy as a way of making the code more robust.
My reasoning is this.
Consider some library code that defines a class Concrete1 and uses it in the inferface to this function.
void do_something( const Concrete1& c );
Passing the type be reference is for efficiency and is, in general, a good idea. If the library considers Concrete1 to be a value type the implementation may decided to make a copy of the input parameter.
void do_something( const Concrete1& c )
{
// ...
some_storage.push_back( c );
// ...
}
If the object type of the passed reference is, indeed, Concrete1 and not some other derived type then this code is fine, no slicing is performed. A general warning on this push_back function invocation might produce only false positives and would most likely be unhelpful.
Consider some client code that derives Concrete2 from Concrete1 and passes it into another function.
void do_something_else( const Concrete1& c );
Because the parameter is taken by reference no slicing occurs here on the parameter to check, so it would not be correct to warn here of slicing as it may be that no slicing occurs. Passing in a derived type to a function that takes a reference or pointer is a common and useful way to take advantage of polymorphic types so warning or disallowing this would seem counter-productive.
So where is there error? Well the 'mistake' is passing in a reference to something that is derived from a class that is then treated as though it is a value type by the called function.
There is, in general, no way to generate a consistently useful compile time warning against object slicing and this is why the best defence, where possible, is to eliminate the problem by design.

This is commonly called Object Slicing and is a well-known enough problem to have its own Wikipedia article (although it is only a short description of the problem).
I believe I have used a compiler that had a warning you could enable to detect and warn about this. However, I don't recall which one that was.

Not really a solution to your immediate problem, but....
Most functions that take class/struct objects as parameters should declare the parameters to be of type "const X&" or "X&", unless they have a very good reason not to.
If you always do this, object slicing will never be a problem (references don't get sliced!).

class Derived: public Base{};
You're saying Derived IS a Base, and so it should work in any function that takes a base. If this is a real problem, maybe inheritance isn't what you really want to be using.

I modified your code slightly:
class Base{
public:
Base() {}
explicit Base(const Base &) {}
};
class Derived: public Base {};
void Func(Base)
{
}
//void Func(Derived)
//{
//
//}
//main
int main() {
Func(Derived());
}
The explicit keyword will make sure that the constructor will not be used as an implicit conversion operator - you have to call it explicitly when you want to use it.

Related

Advantages of typedef over derived class?

Simply put, what are the (or are there any) differences between doing say
class MyClassList : list<MyClass> { };
vs
typedef list<MyClass> MyClassList;
The only advantage that I can think of (and its what lead me to this question) is that with the derived class i can now easily forward declare MyClassList as
class MyClassList;
without compiler error, instead of
class MyClass;
typedef list<MyClass> MyClassList;
I can't think of any differences, but this made me wonder, are there cases in which a typedef can be used that a simple derived class can't?
Or to put it another way, is there any reason why I shouldn't change all my typedef list<...> SomeClassList; to the simple derived class so that I can easily forward declare them?
In C++ it is NOT recommended to derive from an STL container, so don't do it.
A typedef is just creating an alias for an existing type, as it were, so typedef std::list<MyClass> MyClassList; creates a "new type" that is called MyClassList which you can now use as follows:
MyClassList lst;
Changing your typedefs to a derived class is a bad idea. Don't do it.
typedef is intended exactly for this purpose -- to alias type names. Its very idiomatic and won't confuse anybody familiar with C++.
But to address why inheriting may be a bad idea.
std::list does not have a virtual destructor. Meaning MyClassList wouldn't have its destructor called when deleted through the base class. So this is typically frowned upon. In your case, you have no intention of putting any members in MyClassList, so this becomes a moot point until the next programmer sees inheritance as an invitation to add new members/override functions etc. They may not realize that std::list's destructor is not virtual and not realize that in some cases MyClassList's destructor won't get called.
Well, a typedef can only do what its name suggests while a derived class can possibly be a full-blown makeover of its base(s). So while there may not be much of a difference if you limit yourself to "just" deriving (and not add any members, or override anything, etc) as far as the compiler is concerned, there might be a big difference as far as human readers of the code are concerned.
One might wonder "why is this a derived class when a typedef would suffice"? Most people would assume that there must be a reason, so you would make life harder to the code's future maintainers. A typedef, on the other hand, is a very specific tool and does not raise questions.
And while we 're on the topic of maintenance don't forget that as most things in C++, this "nothing will go wrong as long as we are disciplined and don't cross this line" is an open invitation to disaster. Since the compiler isn't there to stop you, someone, someday, will cross the line.
A number of things have been mentioned. A big thing however:
Deriving from a type does not inherit all the constructors.
If there are a number of non-default constructors, you won't have them when inheriting (you'd have to forward them to the base constructor).
Typedefs have no such 'issue'.
Now, typedefs do not generate unique typeids. If you want that, and not have the overhead or other disadvantages of inheritance, look at boost: it has a strong typedef macro that generates a unique typeid:
http://www.boost.org/doc/libs/1_37_0/boost/strong_typedef.hpp
A typedef is an alias, while a class is a new type.
In the first case, the compiler has to simply replace MyClassList with list<MyClass>.
In the second case, MyClassList involve the generation of default constructor, copy constructor assignment operator, destructor, and - where c++11 is in use - even move constructor and move assignment.
In default cases, since MyClassList has no additional functionality, optimization will most likely wipe them out.
Note: I found the "deriving classes with non virtual destructor is not recommended" argument a weak one. A C++ developer should know that derivation does not necessarily imply polymorphism. A class that is not deleted through a pointer to its base doesn't need a virtual destructor, like a class whose method is not designed to be "called" through a base pointer does not require that method to be virtual.
Simply, if a destructor is not virtual, don't treat that type as "polymorphic" on deletion.
In this sense, destructor are not different from other virtual or non virtual methods.
If this argument has to be considered strong, then, all classes that don't have "all virtual" method shouldn't be derived!

Is std::unique_ptr<T> required to know the full definition of T?

I have some code in a header that looks like this:
#include <memory>
class Thing;
class MyClass
{
std::unique_ptr< Thing > my_thing;
};
If I include this header in a cpp that does not include the Thing type definition, then this does not compile under VS2010-SP1:
1>C:\Program Files (x86)\Microsoft
Visual Studio
10.0\VC\include\memory(2067): error C2027: use of undefined type 'Thing'
Replace std::unique_ptr by std::shared_ptr and it compiles.
So, I'm guessing that it's the current VS2010 std::unique_ptr's implementation that requires the full definition and it's totally implementation-dependant.
Or is it? Is there something in it's standard requirements that makes impossible for std::unique_ptr's implementation to work with a forward declaration only? It feels strange as it should only hold a pointer to Thing, shouldn't it?
Adopted from here.
Most templates in the C++ standard library require that they be instantiated with complete types. However shared_ptr and unique_ptr are partial exceptions. Some, but not all of their members can be instantiated with incomplete types. The motivation for this is to support idioms such as pimpl using smart pointers, and without risking undefined behavior.
Undefined behavior can occur when you have an incomplete type and you call delete on it:
class A;
A* a = ...;
delete a;
The above is legal code. It will compile. Your compiler may or may not emit a warning for above code like the above. When it executes, bad things will probably happen. If you're very lucky your program will crash. However a more probable outcome is that your program will silently leak memory as ~A() won't be called.
Using auto_ptr<A> in the above example doesn't help. You still get the same undefined behavior as if you had used a raw pointer.
Nevertheless, using incomplete classes in certain places is very useful! This is where shared_ptr and unique_ptr help. Use of one of these smart pointers will let you get away with an incomplete type, except where it is necessary to have a complete type. And most importantly, when it is necessary to have a complete type, you get a compile-time error if you try to use the smart pointer with an incomplete type at that point.
No more undefined behavior
If your code compiles, then you've used a complete type everywhere you need to.
class A
{
class impl;
std::unique_ptr<impl> ptr_; // ok!
public:
A();
~A();
// ...
};
Type completeness requirements for unique_ptr and shared_ptr
shared_ptr and unique_ptr require a complete type in different places. The reasons are obscure, having to do with a dynamic deleter vs a static deleter. The precise reasons aren't important. In fact, in most code it isn't really important for you to know exactly where a complete type is required. Just code, and if you get it wrong, the compiler will tell you.
However, in case it is helpful to you, here is a table which documents several operations of shared_ptr and unique_ptr with respect to completeness requirements.
Operation
unique_ptr
shared_ptr
P() (default constructor)
incomplete
incomplete
P(const P&) (copy constructor)
—
incomplete
P(P&&) (move constructor)
incomplete
incomplete
~P() (destructor)
complete
incomplete
P(A*) (constructor from ptr)
incomplete
complete
operator=(const P&) (copy assignment)
—
incomplete
operator=(P&&) (move assignment)
complete
incomplete
reset()
complete
incomplete
reset(A*)
complete
complete
Any operations requiring pointer conversions require complete types for both unique_ptr and shared_ptr.
The unique_ptr<A>{A*} constructor can get away with an incomplete A only if the compiler is not required to set up a call to ~unique_ptr<A>(). For example if you put the unique_ptr on the heap, you can get away with an incomplete A. More details on this point can be found in BarryTheHatchet's answer here.
The compiler needs the definition of Thing to generate the default destructor for MyClass. If you explicitly declare the destructor and move its (empty) implementation to the CPP file, the code should compile.
This isn't implementation-dependent. The reason that it works is because shared_ptr determines the correct destructor to call at run-time - it isn't part of the type signature. However, unique_ptr's destructor is part of its type, and it must be known at compile-time.
It looks like current answers are not exactly nailing down why default constructor (or destructor) is problem but empty ones declared in cpp isn't.
Here's whats happening:
If outer class (i.e. MyClass) doesn't have constructor or destructor then compiler generates the default ones. The problem with this is that compiler essentially inserts the default empty constructor/destructor in the .hpp file. This means that the code for default contructor/destructor gets compiled along with host executable's binary, not along with your library's binaries. However this definitions can't really construct the partial classes. So when linker goes in your library's binary and tries to get constructor/destructor, it doesn't find any and you get error. If the constructor/destructor code was in your .cpp then your library binary has that available for linking.
This is nothing to do with using unique_ptr or shared_ptr and other answers seems to be possible confusing bug in old VC++ for unique_ptr implementation (VC++ 2015 works fine on my machine).
So moral of the story is that your header needs to remain free of any constructor/destructor definition. It can only contain their declaration. For example, ~MyClass()=default; in hpp won't work. If you allow compiler to insert default constructor or destructor, you will get a linker error.
One other side note: If you are still getting this error even after you have constructor and destructor in cpp file then most likely the reason is that your library is not getting compiled properly. For example, one time I simply changed project type from Console to Library in VC++ and I got this error because VC++ did not added _LIB preprocessor symbol and that produced exact same error message.
Just for completeness:
Header: A.h
class B; // forward declaration
class A
{
std::unique_ptr<B> ptr_; // ok!
public:
A();
~A();
// ...
};
Source A.cpp:
class B { ... }; // class definition
A::A() { ... }
A::~A() { ... }
The definition of class B must be seen by constructor, destructor and anything that might implicitely delete B.
(Although the constructor doesn't appear in the list above, in VS2017 even the constructor needs the definition of B. And this makes sense when considering that in case of an exception in the constructor the unique_ptr is destroyed again.)
I was looking for a way to use the PIMPL idiom with std::unique_ptr. This guide is a great resource.
In short, here's what you can do to make it work:
my_class.h
#include <memory>
class Thing;
class MyClass
{
~MyClass(); // <--- Added
std::unique_ptr< Thing > my_thing;
};
my_class.cpp
MyClass::~MyClass() = default; // Or a custom implementation
The full definition of the Thing is required at the point of template instantiation. This is the exact reason why the pimpl idiom compiles.
If it wasn't possible, people would not ask questions like this.
The simple answer is just use shared_ptr instead.
As for me,
QList<QSharedPointer<ControllerBase>> controllers;
Just include the header ...
#include <QSharedPointer>

Opt-out of copy constructor

This might be a silly question, but...
I've been writing a number of classes that utilize non-copyable members. These classes are never initialized via the copy constructor in my source. When I try to compile without supplying my own copy-constructor, g++ throws out many errors about how it can't build a default copy constructor, due to the non-copyable member objects.
Is there a way to tell the compiler to just not give me a copy constructor?
EDIT: Yeah... feels silly... I had a case where I was invoking the copy-constructor by accident in a boost::bind call. Lesson learned.
The usual way to make things noncopyable is to declare but not define a copy constructor, and make it private so nothing can call it.
The next revision of the language will provide an explicit way to suppress these generated functions.
If you don't actually cause the copy-constructor to be called then it is not an error if the compiler would be unable to generate one. It sounds like you are (possibly indirectly) causing the copy-constructor to be used.
You can suppress the compiler generated one by declaring your own copy-constructor (you don't need to define it if you're not using it). You can place it in the private section of your class.
If this changes the error to say that the copy-constructor is inaccessible or you get link errors then you really are causing the copy-construtor to be used and you need to analyze why this is.
Not in the current version of C++. In C++ 0x, there will be an =delete; syntax to tell it that you don't want one of the special member functions the compiler will generate by default if you don't defined one yourself.
Until the new C++ 0x standard is fully supported, the best you can do is to delclare a version of the special member function, but not implement them. Normally they are made private (to help make it clear that they shouldn't be used).
Class foo
{
// ... rest of definition
private:
foo (const foo& rhs); // Do Not Implement
const foo& operator= (const foo& rhs); // Do Not Implement
};
No :)
If you want your class to be non-copyable use something like boost::noncopyable
class MyClass : private boost::noncopyable
{
}
or use a parametrizied macro in your class definition that declares a private copy constructor.

Macros to disallow class copy and assignment. Google -vs- Qt

To disallow copying or assigning a class it's common practice to make the copy constructor
and assignment operator private. Both Google and Qt have macros to make this easy and visible.
These macros are:
Google:
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
TypeName(const TypeName&); \
void operator=(const TypeName&)
Qt:
#define Q_DISABLE_COPY(Class) \
Class(const Class &); \
Class &operator=(const Class &);
Questions:
Why are the signatures of the two assignment operators different? It seems like the Qt version is correct.
What is the practical difference between the two?
It doesn't matter. The return type is not part of a function's signature, as it does not participate in overload resolution. So when you attempt to perform an assignment, both declarations will match, regardless of whether you use the return type.
And since the entire point in these macros is that the functions will never get called, it doesn't matter that one returns void.
I'd just like to mention that there is an alternative strategy for implementing an abstraction for disallowing copy and assignment of a class. The idea is to use inheritance instead of the preprocessor. I personally prefer this approach as I follow the rule of thumb that it is best to avoid using the preprocessor when at all possible.
boost::noncopyable is an example implementation. It is used as follows:
class A : noncopyable
{
...
};
See Boost.Utility, specifically boost::noncopyable. It's not a macro but a base class with private copy and assignment. It prevents the compiler from generating implicit copy and assignment in derived classes.
edit: Sorry, this was not an answer to the original question. By the way, boost::noncopyable uses a const reference as return type for the assignment operator. I was under the impression that the type of the return value doesn't matter since it's not supposed to be used. Still, making the operator private doesn't prevent usage inside the class or friends in which case a non-usual return type (like void, a const reference, etc) might lead to compilation errors and catch additional bugs.
There's no practical difference. The assignment operator signatures differ just as a matter of style. It's usual to have an assignment operator returning a reference to allow chaining:
a = b = c;
but a version returning void is also legal and will work just fine for cases when the only purpose is to just declare the operator private and therefore prohibited to use.
From the standard, 12.8, clause 9: "A user-declared copy assignment operator X::operator= is a non-static non-template member function of class X with exactly one parameter of type X, X&, const X&, volatile X&, or const volatile X&." It says nothing about the return type, so any return type is permissible.
Clause 10 says "If the class definition does not explicitly declare a copy assignment operator, one is declared implicitly."
Therefore, declaring any X::operator=(const X&) (or any other of the specified assignment types) is sufficient. Neither the body nor the return type is significant if the operator will never be used.
Therefore, it's a stylistic difference, with one macro doing what we'd likely expect and one saving a few characters and doing the job in a way that's likely to surprise some people. I think the Qt macro is better stylistically. Since we're talking macro, we're not talking about the programmer having to type anything extra, and failing to surprise people is a good thing in a language construct.
Others have already answered why it's legal to have different return values for operator=; IMHO jalf said it best.
However, you might wonder why Google uses a different return type, and I suspect it's this:
You don't have to repeat the type name when disabling the assignment operator like this. Usually the type name is the longest part of the declaration.
Of course, this reason is void given that a macro is used but still - old habits die hard. :-)
Both serve the same purpose
Once you write this one:
Class &operator=(const Class &);
you will get the benefits of chain assignments. But in this case you want the assignment operator to be private. so it doesn't matter.
Qt version is backward compatible, while google's is not.
If you develop your library and deprecate the use of assignment before you completely remove it, in Qt it will most likely retain the signature it originally had. In this case older application will continue to run with new version of library (however, they won't compile with the newer version).
Google's macro doesn't have such a property.
As several other answers have mentioned, the return type of the function doesn't participate in the function signature, so both declarations are equivalent as far as making the assignment operator unusable by clients of the class.
Personally I prefer the idiom of having a class privately inherit from an empty non-copyable base class (like boost::noncopyable, but I have my own so I can use it in projects that don't have boost available). The empty base class optimization takes care of making sure there's zero overhead, and it's simple, readable, and doesn't rely on the dreaded preprocessor macro functionality.
It also has the advantage that copy and assignment can't even be used within class implementation code - it'll fail at compile time while these macros will fail at link time (likely with a less informative error message).
Incidentally, if you have access to the Boost libraries (You don't? Why the heck not??), The Utility library has had the noncopyable class for a long time:
class YourNonCopyableClass : boost::noncopyable {
Clearer IMHO.
In practice I would say that both should not be used anymore if you have a C++11 compiler.
You should instead use the delete feature , see here
Meaning of = delete after function declaration
and here
http://www.stroustrup.com/C++11FAQ.html#default
Why : essentially because compiler message is much more clearer. When the compiler need one of the copy or copy assignment operator, it immediately points out to the line where the =delete was coded.
Better and complete explanations can also be found in Item 11: Prefer deleted functions to private undefined ones from Effective Modern C++ book by Scott Meyers

Why is is it not possible to pass a const set<Derived*> as const set<Base*> to a function?

Before this is marked as duplicate, I'm aware of this question, but in my case we are talking about const containers.
I have 2 classes:
class Base { };
class Derived : public Base { };
And a function:
void register_objects(const std::set<Base*> &objects) {}
I would like to invoke this function as:
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?
Edit:
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.
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.
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();
};
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 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.
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());
[Edit: you've changed your code sample not to pass by value. My code still works, and afaik is the best you can do other than refactoring the calling code to use a std::set<Base*> in the first place.]
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);
(*) 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.
If your register_objects function receives an argument, it can put/expect any Base subclass in there. That's what it's signature sais.
It's a violation of the Liskov substitution principle.
This particular problem is also referred to as Covariance. In this case, where your function argument is a constant container, it could be made to work. In case the argument container is mutable, it can't work.
Take a look here first: Is array of derived same as array of base. In your case set of derived is a totally different container from set of base and since there is no implicit conversion operator is available to convert between them , compiler is giving an error.
std::set<Base*> and std::set<Derived*> are basically two different objects. Though the Base and Derived classes are linked via inheritance, at compiler template instantiation level they are two different instantiation(of set).
Firstly, It seems a bit odd that you aren't passing by reference ...
Secondly, as mentioned in the other post, you would be better off creating the passed-in set as a std::set< Base* > and then newing a Derived class in for each set member.
Your problem surely arises from the fact that the 2 types are completely different. std::set< Derived* > is in no way inherited from std::set< Base* > as far as the compiler is concerned. They are simply 2 different types of set ...
Well, as stated in the question you mention, set<Base*> and set<Derived*> are different objects. Your register_objects() function takes a set<Base*> object. So the compiler do not know about any register_objects() that takes set<Derived*>. The constness of the parameter does not change anything. Solutions stated in the quoted question seem the best things you can do. Depends on what you need to do ...
As you are aware, the two classes are quite similar once you remove the non-const operations. However, in C++ inheritance is a property of types, whereas const is a mere qualifier on top of types. That means that you can't properly state that const X derives from const Y, even when X derives from Y.
Furthermore, if X does not inherit from Y, that applies to all cv-qualified variants of X and Y as well. This extends to std::set instantiations. Since std::set<Foo> does not inherit from std::set<bar>, std::set<Foo> const does not inherit from std::set<bar> const either.
You are quite right that this is logically allowable, but it would require further language features. They are available in C# 4.0, if you're interested in seeing another language's way of doing it. 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
Didn't see it linked yet, so here's a bullet point in the C++ FAQ Lite related to this:
http://www.parashift.com/c++-faq-lite/proper-inheritance.html#faq-21.3
I think their Bag-of-Apples != Bag-of-Fruit analogy suits the question.