Note: This is a follow-up question to this.
I have a group of template classes that do completely different things in completely different ways using completely different datatypes. They do, however, share common method names. For example, Get(), Set(), Resize(), etc. are valid methods for each of the classes in question. Additionally, they accept arguments in the same order. This allows for generalized non-friend, non-member functions to work on each of the classes. A simplified example:
template <typename Class, typename Datatype>
void Insert(const Class<Datatype>& Object, const std::size_t Index, const Datatype Value)
{
Object.Resize(Object.Size() + 1);
for (std::size_t CurrentIndex = Object.Size() - 1; CurrentIndex > Index; CurrentIndex--)
{
Object.Set(CurrentIndex, Object.Get(CurrentIndex - 1));
}
Object.Set(Index, Value);
}
Right now, I'm just relying on my own memory to define all the appropriate methods properly. Is there a way to have the compiler enforce the definition of the proper methods? If not, is there a better way to do this?
What you are looking for is called "concepts" and used to be a feature in C++0x but got dropped from the new standard.
There are some implementations for C++03 out there, but they are harder to use and might be not worth the trouble. e.g. Boost Concept Checking
gcc also has the --enable-concept-check option although I'm not entirely sure how that works with user code.
The compiler already enforces the definition of those methods by refusing to let you instantiate the template for types that don't provide the necessary methods.
Unfortunately, compilers' error messages for uninstantiable templates are often difficult to decipher.
You can document the type requirements in comments. Take a look at how the C++ standard defines type requirements like Assignable, CopyConstructible, EqualityComparable, LessThanComparable, and the requirements for types in the standard containers.
The compiler will enforce the correct interface by failing to compile any call to a nonexistent function; perhaps the problem is that the error messages are too cryptic?
You could define a base class which declares the required interface as non-virtual functions. The base class functions have no definitions (except where it makes sense to have an optional function with a default implementation).
Then, if the template argument derives from this base class, failure to implement a required function will cause a link error (due to attempting to call the base class functions, which are not defined). This will most likely be easier to diagnose than a typical template-related compile error.
You could go one step further and include a compile-time check that the template argument is derived from the base class; I'll leave that as an exercise for the reader. Or it might be better to just document the purpose of the base class, and leave it up to the user whether to use it or not.
You can use an interface.
See this question: How do you declare an interface in C++?
Related
Does C++20 offer any new solutions to the problem of public member invisibility and source code bloat/repetition with inherited class templates described in this question over 2 years ago ?
The "Problem"
The alleged "problem" is that, in a template, an unqualified name used in a way which isn't dependent on the specialization is truly independent of the specialization and refers to the entity with that name found at that point. The alleged source code "bloat" is using this-> to explicitly make the name dependent or qualifying the name. This is still the situation in C++20.
Just to be clear, the set of entities is not known at the point we refer to them. In the linked question, the base class depends on the template parameter, and we have only seen the primary base class template. The base class template may be specialized later and may have completely different member functions than the ones we've seen. So, any "solution" requires that a name without any obvious contextual dependence on the specialization find entities not yet declared which may be surprising.
Why It's Impossible
Any naive changes in this direction are either pretty big or have severe downsides, or both.
You could postpone all name lookup until instantiation time, discarding two phase lookup. That invites ODR violations which is silent UB, which is a huge downside.
You could restrict specialization so that you cannot specialize the base class later such that a different entity is found. That is difficult to diagnose, so it would likely be a new rule introducing silent UB.
You could opt in with a using declaration: using X::* as you propose or using class X as someone else suggested in a different context. This has the benefit of explicitness. It moves the problem a level up: if X is not dependent, presumably it should be found now, but if it is dependent, what happens? We can't instantiate it prior to instantiating the template we're in now. Thus, we can't interpret any names we see until instantiation. It has similar downsides to discarding two phase lookup.
Any of these changes would add complexity to an already complex area and would also pose significant backwards compatibility hurdles. None of them is a clear win.
Note: In C++20 they did make the rules more uniform by allowing ADL to find function templates with specified parameters to be found: f<int>(1).
Why It's Not a Problem
I doubt there would be consensus that this really is a problem. The linked question makes a poor argument. The derived class adds member function behavior to a certain base class, but a free function works better. These behaviors did not need member access, they aren't required by the language to be members, they can be found as non-members with ADL, and by using a free function they apply even when the static type you have is the base type. So, using inheritance for this is unnecessary coupling, and is a worse option.
Searching for 100s of places to add this-> and adding these 6 characters 100s of times strikes me as Code Bloat and Repetition when I have to templatize a base class
"Searching": The compiler will tell you when a name can't be found, which is better than silent bad behavior, such as making ODR violations easier to hit.
"templatize a base": Templatizing the base class doesn't trigger this. Templatizing the derived class and making the base dependent does. Yes, when templatizing the derived class, specifying that a bare name used in a way that is independent of the template parameter is in fact dependent may seem like boilerplate to some, but others might argue being explicit is clearer.
"100s of times": Seems hyperbolic.
These code patterns are used all the time in real world. Just look at CRTP. (comment on linked question)
Again, this only applies if the derived class is templated. I would dispute the commonality, but these idioms do exist and have a place.
Most importantly, though, is that CRTP is not a goal. CRTP is a hack. It's a C++ idiom because C++ lacks better facilities. CRTP allows a class to opt into certain behaviors that would otherwise be bothersome to write. Relevant C++ proposals do exist, but by and large, they have focused on making extension easier or removing boilerplate, and not on making CRTP, the hack, easier.
These are some that come to mind:
C++20: Comparisons
A very common use of CRTP is for things that require a lot of extra boilerplate. C++ required you to define operator== and operator!=, for instance. By opting into a CRTP base class, one could define only the primitive operation and have the other one generated.
C++20 fixed the underlying problem with comparisons. The typical member-wise comparison can be defaulted, and comparisons can be re-written so that != can invoke ==.
The problem is solved at the root, removing CRTP, not enhancing it.
C++20: Iterators to Ranges
Another common use of CRTP in the same vein as above is iterators. Writing custom iterators requires only a few fundamental operations: advance and dereference for forward iterators. But then there's a lot of extra seemingly unnecessary ceremony: pre-increment, post-increment, const iterators, typedefs, etc.
C++20 took a large step forward by introducing range concepts and the range library. The result is that it should be much less necessary to write a custom iterator. Ranges become a capable concept on their own, and there's a good suite of range combinators.
C++20: Concepts
C++ essentially has two systems for specifying an interface: virtual functions and concepts. They have trade-offs. Virtual functions are intrusive. But prior to C++20, concepts were implicit and emulated. One reason to use CRTP or inheritance generally would be to inject virtual functions. But in C++20, concepts are a language feature removing one big negative.
Future C++: Metaclasses
One value of CRTP in addition to the boilerplate reduction is satisfying an entire collection of multiple type requirements. A comparable class defines all the comparison operators. An clonable base defines a virtual destructor and clone.
This is the topic of metaclasses, which is not yet in C++.
See Also
See also the work on customization points which seems very interesting. And see the debates on unified function call syntax, which seems like we'll never get.
Summary
There's a very good question hiding in here about how C++20 makes it easier to reduce boilerplate, remove hacks like CRTP, and write better and clearer code. C++20 takes several steps in this regard, but they made the expression of intent easier, not a particular idiom.
Below is a very simple example of what I understand as static polymorphism. The reason why I'm not using dynamic polymorphism is that I do not want to obstruct inlining of functions of PROCESSOR in op.
template <class PROCESSOR>
void op(PROCESSOR* proc){
proc->doSomething(5);
proc->doSomethingElse();
}
int main() {
ProcessorY py;
op<ProcessorY>(&py);
return 0;
}
The problem with this example is: There exists no explicit definition of what functions a PROCESSOR has to define. If one is missing, you will just get a compile error. I think this is bad style.
It also has a very practical drawback: On-line assistance of IDEs cannot of course show you the functions that are available on that object.
What is a good/official way to define the public interface of a PROCESSOR?
There exists no explicit definition of what methods a PROCESSOR has to define. If one is missing, you will just get a compile error. I think, this is bad style.
It is. It is not. It may be. It depends.
Yes, if you want to define behavior in this way, you may want to also restrict the content, that template parameters should have. Unfortunately, it is not possible to do this "explicitly" right now.
What you want is constraints and concepts feature, that was supposed to appear as part of C++ 11, but was delayed and is still not available as of C++ 14.
However, getting compile-time error is often the best way to restrict template parameters. As an example, we can use std library:
1) Iterators.
C++ library defines few types of iterators: forward_iterator, random_access_iterator and others. For each type, there is a set of properties and valid expressions defined, that are guaranteed to be available. If you used iterator, that is not fully compatible with random_access_iterator in container, that requires random_access_iterator, you will get compiler error at some point (most likely, when using dereference operator ([]), which is required in this iterator class).
2) Allocators.
All containers in std library use allocator to perform memory allocation/deallocation and objects construction. By default, std::allocator is used. If you want to exchange it with your own, you need to ensure, that it has everything, that std::allocator is guaranteed to have. Otherwise, you will get compile-time error.
So, until we get concepts, this is the best and most widely used solution.
First, I think there is no problem with your static polymorphism example. Since it's static, i.e. compile-time resolved, by definition it has less strict demands regarding its interface definition.
Also it's absolutely legitimate that the incorrect code just won't compile/link, though a more clear error message from the compiler would be nicer.
If you, however, insist on interface definition, you may rewrite your example the following way:
template <class Type>
class Processor
{
public:
void doSomething(int);
void doSomethingElse();
};
template <class Type>
void op(Processor<Type>* proc){
proc->doSomething(5);
proc->doSomethingElse();
}
// specialization
template <>
class Processor<Type_Y>
{
// implement the specialized methods
};
typedef Processor<Type_Y> ProcessorY;
int main() {
ProcessorY py;
op(&py);
return 0;
}
In this question the OP asked about limiting what classes a template will accept. A summary of the sentiment that followed is that the equivalent facility in Java is bad; and don't do this.
I don't understand why this is bad. Duck typing is certainly a powerful tool; but in my mind it lends itself confusing runtime issues when a class looks close (same function names) but has slightly different behavior. And you can't necessarily rely on compile time checking because of examples like this:
struct One { int a; int b };
struct Two { int a; };
template <class T>
class Worker{
T data;
void print() { cout << data.a << endl; }
template <class X>
void usually_important () { int a = data.a; int b = data.b; }
}
int main() {
Worker<Two> w;
w.print();
}
Type Two will allow Worker to compile only if usually_important is not called. This could lead to some instantiations of Worker compiling and others not even in the same program.
In a case like this, though. The responsibility is put on to the designer of ENGINE to ensure that it is a valid type (after which they should inherit ENGINE_BASE). If they don't, there will be a compiler error. To me this seems much safer while not imposing any restrictions or adding much additional work.
class ENGINE_BASE {}; // Empty class, all engines should extend this
template <class ENGINE>
class NeedsAnEngine {
BOOST_STATIC_ASSERT((is_base_of<ENGINE_BASE, ENGINE>));
// Do stuff with ENGINE...
};
This is too long, but it might be informative.
Generics in Java are a type erasure mechanism, and automatic code generation of type casts and type checks.
templates in C++ are code generation and pattern matching mechanisms.
You can use C++ templates to do what Java generics do with a bit of effort. std::function< A(B) > behaves in a covariant/contravariant fashion with regards to A and B types and conversion to other std::function< X(Y) >.
But the primary design of the two is not the same.
A Java List<X> will be a List<Object> with some thin wrapping on it so users don't have to do type casts on extraction. If you pass it as a List<? extends Bar>, it again is getting a List<Object> in essence, it just has some extra type information that changes how the casts work and which methods can be invoked. This means you can extract elements from the List into a Bar and know it works (and check it). Only one method is generated for all List<? extends Bar>.
A C++ std::vector<X> is not in essence a std::vector<Object> or std::vector<void*> or anything else. Each instance of a C++ template is an unrelated type (except template pattern matching). In fact, std::vector<bool> uses a completely different implementation than any other std::vector (this is now considered a mistake because the implementation differences "leak" in annoying ways in this case). Each method and function is generated independently for the particular type you pass it.
In Java, it is assumed that all objects will fit into some hierarchy. In C++, that is sometimes useful, but it has been discovered it is often ill fitting to a problem.
A C++ container need not inherit from a common interface. A std::list<int> and std::vector<int> are unrelated types, but you can act on them uniformly -- they both are sequential containers.
The question "is the argument a sequential container" is a good question. This allows anyone to implement a sequential container, and such sequential containers can as high performance as hand-crafted C code with utterly different implementations.
If you created a common root std::container<T> which all containers inherited from, it would either be full of virtual table cruft or it would be useless other than as a tag type. As a tag type, it would intrusively inject itself into all non-std containers, requiring that they inherit from std::container<T> to be a real container.
The traits approach instead means that there are specifications as to what a container (sequential, associative, etc) is. You can test these specifications at compile time, and/or allow types to note that they qualify for certain axioms via traits of some kind.
The C++03/11 standard library does this with iterators. std::iterator_traits<T> is a traits class that exposes iterator information about an arbitrary type T. Someone completely unconnected to the standard library can write their own iterator, and use std::iterator<...> to auto-work with std::iterator_traits, add their own type aliases manually, or specialize std::iterator_traits to pass on the information required.
C++11 goes a step further. for( auto&& x : y ) can work with things that where written long before the range-based iteration was designed, without touching the class itself. You simply write a free begin and end function in the namespace that the class belongs to that returns a valid forward iterator (note: even invalid forward iterators that are close enough work), and suddenly for ( auto&& x : y ) starts working.
std::function< A(B) > is an example of using these techniques together with type erasure. It has a constructor that accepts anything that can be copied, destroyed, invoked with (B) and whose return type can be converted to A. The types it can take can be completely unrelated -- only that which is required is tested for.
Because of std::functions design, we can have lambda invokables that are unrelated types that can be type-erased into a common std::function if needed, but when not type erased their invokation action is known from there type. So a template function that takes a lambda knows at the point of invokation what will happen, which makes inlining an easy local operation.
This technique is not new -- it was in C++ since std::sort, a high level algorithm that is faster than C's qsort due to the ease of inlining invokable objects passed as comparators.
In short, if you need a common runtime type, type erase. If you need certain properties, test for those properties, don't force a common base. If you need certain axioms to hold (untestable properties), either document or require callers to claim those properties via tags or traits classes (see how the standard library handles iterator categories -- again, not inheritance). When in doubt, use free functions with ADL enabled to access properties of your arguments, and have your default free functions use SFINAE to look for a method and invoke if it exists, and fail otherwise.
Such a mechanism removes the central responsibility of a common base class, allows existing classes to be adapted without modification to pass your requirements (if reasonable), places type erasure only where it is needed, avoids virtual overhead, and ideally generates clear errors when properties are found to not hold.
If your ENGINE has certain properites it needs to pass, write a traits class that tests for those.
If there are properties that cannot be tested for, create tags that describe such properties. Use specialization of a traits class, or canonical typedefs, to let the class describe which axioms hold for the type. (See iterator tags).
If you have a type like ENGINE_BASE, don't demand it, but instead use it as a helper for said tags and traits and axiom typedefs, like std::iterator<...> (you never have to inherit from it, it simply acts as a helper).
Avoid over specifying requirements. If usually_important is never invoked on your Worker<X>, probably your X doesn't need a b in that context. But do test for properties in a way clearer than "method does not compile".
And sometimes, just punt. Following such practices might make things harder for you -- so do an easier way. Most code is written and discarded. Know when your code will persist, and write it better and more extendably and more maintainably. Know that you need to practice those techniques on disposable code so you can write it correctly when you have to.
Let me turn the question around on you: Why is it bad that the code compiles for Two if usually_important isn't called? The type you gave it meets all the needs for that particular instantiation and the compiler will immediately tell you if a particular instantiation no longer meets the interface needed for the needed functionality in the template.
That said if you insist that you need an Engine object, don't do it with templates at all, instead treat it as a sort of strategy pattern with a non-template (using this approach enforces at compile time that the user-defined type adheres to a specific interface, not just that it looks like a duck):
class Worker
{
public:
explicit Worker(EngineBase* data) : data_(data) {}
void print() { cout << data_->a() << endl; }
template <class X>
void usually_important () { int a = data_->a(); int b = data_->b(); }
private:
EngineBase* data_;
}
int main()
{
Worker w(new ConcreteEngine);
w.print();
}
I don't understand why this is bad. Duck typing is certainly a
powerful tool; but in my mind it lends itself confusing runtime issues
when a class looks close (same function names) but has slightly
different behavior.
The probability that you can define a non-trivial interface and then by accident have another interface that has different semantics but can be substituted is minimal. This never, ever happens.
Type Two will allow Worker to compile only if usually_important is not
called.
That is a good thing. We depend on it all the time. It makes class templates more flexible.
Matching a compile-time interface is strictly superior to a run-time one. This is because run-time interfaces can't differ in key ways that compile-time ones can (e.g. different types in the interface), and require a bunch of run-time abstraction like dynamic allocation that may be unnecessary.
In a case like this, though. The responsibility is put on to the
designer of ENGINE to ensure that it is a valid type (after which they
should inherit ENGINE_BASE). If they don't, there will be a compiler
error. To me this seems much safer while not imposing any restrictions
or adding much additional work.
It is not safer. It is utterly pointless. It is stupendously unlikely that the user will accidentally instantiate the class with the wrong type but it will compile successfully due to circumstantial interface match.
What it really boils down to is this: you should only require what you really need. Absolutely definitely must have in order to function. Everything else, don't require it. This is a core tenet of making software maintainable. You cannot possibly imagine what shenanigans I might conceive of long after you have written this class to use it in ways that you never thought it could be used for.
Should I define an interface which explicitly informs the user what all he/she should implement in order to use the class as template argument or let the compiler warn him when the functionality is not implemented ?
template <Class C1, Class C2>
SomeClass
{
...
}
Class C1 has to implement certain methods and operators, compiler won't warn until they are used. Should I rely on compiler to warn or make sure that I do:
Class C1 : public SomeInterfaceEnforcedFunctions
{
// Class C1 has to implement them either way
// but this is explicit? am I right or being
// redundant ?
}
Ideally, you should use a concept to specify the requirements on the type used as a template argument. Unfortunately, neither the current nor the upcoming standard includes concepts.
Absent that, there are various methods available for enforcing such requirements. You might want to read Eric Neibler's article about how to enforce requirements on template arguments.
I'd agree with Eric's assertion that leaving it all to the compiler is generally unacceptable. It's much of the source of the horrible error messages most of us associate with templates, where seemingly trivial typos can result in pages of unreadable dreck.
If you are going to force an interface, then why use a template at all? You can simply do -
class SomeInterface //make this an interface by having pure virtual functions
{
public:
RType SomeFunction(Param1 p1, Param2 p2) = 0;
/*You don't have to know how this method is implemented,
but now you can guarantee that whoever wants to create a type
that is SomeInterface will have to implement SomeFunction in
their derived class.
*/
};
followed by
template <class C2>
class SomeClass
{
//use SomeInterface here directly.
};
Update -
A fundamental problem with this approach is that it only works for types that is rolled out by a user. If there is a standard library type that conforms to your interface specification, or a third party code or another library (like boost) that has classes that conform to SomeInterface, they won't work unless you wrap them in your own class, implement the interface and forward the calls appropriately. I'm somehow not liking my answer anymore.
Absent of concepts, a for now abandoned concept (pun not intended, but noted) for describing which requirements a template parameter must fulfill, the requirements are only enforced implicitly. That is, if whatever your users use as a template parameter doesn't fulfill them, the code won't compile. Unfortunately, the error message resulting from that are often quite gibberish. The only things you can do to improve matters is to
describe the requirements in your template's documentation
insert code that checks for those requirements early on in your template, before it delves so deep that the error messages your users get become unintelligibly.
The latter can be quite complicated (static_assert to the rescue!) or even impossible, which is the reason concepts where considered to become a core-language feature, instead of a library.
Note that it is easy to overlook a requirement this way, which will only become apparent when someone uses a type as a template parameter that won't work. However, it is at least as easy to overlook that requirements are often quite lose and put more into the description than what the code actually calls for.
For example, + is defined not only for numbers, but also for std::string and for any number of user-defined types. Conesequently, a template add<T> might not only be used with numbers, but also with strings and an infinite number of user-defined types. Whether this is an unwanted side-effect of the code you want to suppress or a feature you want to support is up to you. All I'm saying is that it is not easy to catch this.
I don't think defining an interface in the form of an abstract base class with virtual functions is a good idea. This is run-time polymorphism, a main pillar classic OO. If you do this, then you don't need a template, just take the base class per reference.
But then you also lose one of the main advantages of templates, which is that they are, in some ways, more flexible (try to write an add() function classic OO which works with any type overloading + in) and faster, because the binding of the function calls take place not at run-time, but during compilation. (That brings more than it might look like at first due to the ability to inline, which usually isn't possible with run-time polymorphism.)
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.