-
extending an STL container class Question
I have a class, "event_list", derived from the template class "vector." I extended it with a method, "write" for this new class. Here's its definition:
class event_list : public vector<Event>
{
public:
void write (ofstream &filename, format_type format, int start, int end);
};
For what it`s worth, the class "Event" is defined thus:
class Event
{
public:
int date;
string name;
string poc;
string type;
Event();
~Event();
void write(ofstream &filename, int start, int end);
};
In the "write" method for "event_list" (derived from vector), I want to go through every element and write it out. My problem is how to reference vector methods and operators within this method for the derived class. I currently have the following code, which will not compile:
void write (ofstream &filename, format_type format, int start, int end)
{
// write out vector contents
for (int indx = 0; indx < *this.size(); indx++)
{
current_date = *this[indx].date;
if (current_date != last_date)
{
last_date = current_date;
}
*this[indx].write(filename, start, end);
. . .
};
However, the compiler is telling me that this is and invalid use of "this" in non-member function (all 3 uses of "this"). I thought this was the way to do it.
Could anyone point out what I am doing wrong? Thanks, Alan
-
Re: extending an STL container class Question
There is no need to use *this to call vector member functions. Since you're using public inheritance, you can just use the method name itself. For overloaded operators, it's a bit more tricky, you have to write out the operator function's full name (e.g. operator[]).
Here's a version of you code that would work. Note the bolded areas:
Code:
void write (ofstream &filename, format_type format, int start, int end)
{
// write out vector contents
for (int indx = 0; indx < size(); indx++)
{
current_date = operator[](index).date;
if (current_date != last_date)
{
last_date = current_date;
}
operator[](indx).write(filename, start, end);
. . .
};
P.S: Your approach (using "this") would work, too, but you need to put brackets around "*this", because the membership operator has a higher precedence than the pointer dereferencing operator:
Code:
void write (ofstream &filename, format_type format, int start, int end)
{
// write out vector contents
for (int indx = 0; indx < (*this).size(); indx++)
{
current_date = (*this)[indx].date;
if (current_date != last_date)
{
last_date = current_date;
}
(*this)[indx].write(filename, start, end);
. . .
};
However, I think that using *this makes the code less clear (especially because you have to put the brackets around it), so I would stick with the first method.
-
Re: extending an STL container class Question
When I make those changes, the compiler complains that "size" is undeclared. It also complains that "operator[]" is not defined. Do I need to include some header file for the latter to work?
Thanks
-
Re: extending an STL container class Question
The following code compiles correctly on Dev-Cpp:
Code:
#include <vector>
#include <string>
using namespace std;
class Event
{
public:
int date;
string name;
string poc;
string type;
Event();
~Event();
void write(ofstream &filename, int start, int end);
};
class event_list : public vector<Event>
{
public:
void write (ofstream &filename, int start, int end)
{
// write out vector contents
for (int indx = 0; indx < size(); indx++)
{
int last_date;
int current_date = operator[](indx).date;
if (current_date != last_date)
{
last_date = current_date;
}
operator[](indx).write(filename, start, end);
}
}
};
-
Re: extending an STL container class Question
You're gonna get in trouble for deriving publicly from std::vector. ;)
Is there a reason you didn't just make write() a standalone function that took a vector<Event> reference as an argument? Heck, you could make it take iterators and that way your write function would work if you stored your Event objects in a vector, list, set, array, or any of a bunch of other containers.
If there is more to your event_list class, including extra state information, then you might also consider using containment instead of public derivation.
-
Re: extending an STL container class Question
jlou has hit th nail on the head. vector is not designed for public inheritance. It is, however, designed to be extended via separate algorithm functions.
-
Re: extending an STL container class Question
Do you need to publically inherit from vector. You are saying event_list is a vector. Personally I would have prefferred private inheritance or even better composition. These mean "is implemented in terms of" rather than "is a".I see this as a job for composition really but if you want to save a fair bit of coding at the expense of a quick hack then private inheritance would do. At least then you wouldn't be able to polymorphically delete event_list accidentally. Also do you need all those lovely vector functions in your public interface. Surely they would be better for you if they were private and for event_lists own internal use.
-
Re: extending an STL container class Question
Thank you for helping me sort out the syntax.
I use some standard vector member functions elsewhere in the code. I am still learning (obviously), but that is why I used public inheritance with vector. I evidently am still not clear on when to use composition versus inheritance.
I take it from some other stuff I read that the "list" container does not present the same concerns as "vector." Is that right? If so, why? Also, could you explain to me why "vector" is different?
Thanks, Alan
-
Re: extending an STL container class Question
Well in general public inheritance means "is a" or "is fully substitutable for".
Composition means "has a" or "is implemented in terms of".
Private inheritance means virtually the same as composition. You should favour composition over inheritance unless:-
you need to access protected members.
you need to override a virtual function.
There are a few other reasons too. This article helps explain the situation.
-
Re: extending an STL container class Question
Quote:
Originally Posted by jlou
You're gonna get in trouble for deriving publicly from std::vector. ;)
Is there a reason you didn't just make write() a standalone function that took a vector<Event> reference as an argument? Heck, you could make it take iterators and that way your write function would work if you stored your Event objects in a vector, list, set, array, or any of a bunch of other containers.
If there is more to your event_list class, including extra state information, then you might also consider using containment instead of public derivation.
The only time you could get in trouble in this case is if you had a vector<Event>* pointer that pointed to an event_list object alocated on the heap and tried to delete the object using the pointer. I mean, come on, what are the chances of that?
-
Re: extending an STL container class Question
Quote:
Originally Posted by HighCommander4
The only time you could get in trouble in this case is if you had a vector<Event>* pointer that pointed to an event_list object alocated on the heap and tried to delete the object using the pointer. I mean, come on, what are the chances of that?
You keep on riding this, don't you? ;) The point is that the chance is there... Someone might do it. And when they do, it's a very bad thing.
It's fair enough that you know that you can't delete through base class pointers for the classes you write, but other people using your code might not be aware of that. We shouldn't have to check if a class we use has a virtual destructor before we use it like that. Public inheritance, and it's implied IMO.
-
Re: extending an STL container class Question
Quote:
Originally Posted by jlou
Is there a reason you didn't just make write() a standalone function that took a vector<Event> reference as an argument? Heck, you could make it take iterators and that way your write function would work if you stored your Event objects in a vector, list, set, array, or any of a bunch of other containers.
That's what I did in the end. In this particular application, the way I was designing it, I had to pass parameters down to more than one level of function calls. It made things much cleaner and less confusing in this case.
Thanks, Alan
-
Re: extending an STL container class Question
Quote:
Originally Posted by Inspired
That's what I did in the end. In this particular application, the way I was designing it, I had to pass parameters down to more than one level of function calls. It made things much cleaner and less confusing in this case.
Excellent. I think it will make things more flexible for the future as well.
Quote:
Originally Posted by HighCommander4
The only time you could get in trouble in this case is if you had a vector<Event>* pointer that pointed to an event_list object alocated on the heap and tried to delete the object using the pointer. I mean, come on, what are the chances of that?
I meant that Inspired was going to get in trouble from the members of this forum. :D
The bigger problem besides that potentially undefined behavior is that the alternatives are often better. Most of us learn public inheritance early in our C++ development, so it is often the first option considered, but there are better options out there like standalone functions that the OP ended up using.
-
Re: extending an STL container class Question
Quote:
Originally Posted by wien
You keep on riding this, don't you? ;) The point is that the chance is there... Someone might do it. And when they do, it's a very bad thing.
If the ISO Standards Committee would have believed that public inheritance without dynamic binding is risky to use, it would have made dynamic binding the default for public inheritance and left no other alternative. However, the use of dynamic binding has a conisderably high overhead and its overuse can affect a program's performance.
One thing I always liked about C++ as a language is that, unlike Java, you don't have to pay for what you don't need. This is one of those cases where using dynamic binding is not necessary because no one in their right mind would ever use a pointer to a container, never mind assign a derived class object to it and then try to delete the derived class object through the pointer.
Think about it from a conceptual viewpoint: polymorphism (which, in my view, is the only case you need to use dynamic binding, certainly not for all instances of public inheritance) is used when objects of different type share a common interface and we need to be able to use this interface without having to know what each implementation does for each particular derived class. Right? vector<Event> and event_list, however, are classes that do not need to use polymorphism. The interfaces are the same: the OP hasn't redefined a single vector member function. He simply has added functionality in what I believe is the proper way to do so. I mean, why use private inheritance or containment in this case? Then you would need to write code that exposes each public vector member functions that the users of event_list need... what if all of vector's funtionality is needed by the users of event_list?
Quote:
Originally Posted by wien
It's fair enough that you know that you can't delete through base class pointers for the classes you write, but other people using your code might not be aware of that. We shouldn't have to check if a class we use has a virtual destructor before we use it like that. Public inheritance, and it's implied IMO.
Anyone who would try to use a vector<Event>* to point to an event_list object would know that the base class is vector. And everyone should know that STL classes do not have virtual destructors. However, as I asserted above, no one would ever need to do such thing, so their knowing or not knowing whether vector has a virtual destructor is not a problem in the first place.
I think the central idea here is that there is a difference between public inheritance and polymoprhism. Public inheritance does not always mean polymoprhism. It simply means IS-A. And in the OP's case, he is using it correctly: an event_list IS a vector. Since when should you not use public inheritance without polymorphism? Polymorphism is used for very specific needs and, as I mentioned earlier, it has a high overhead, so there is no need to use it unless those specific needs aren't present.
The reason the STL writers did not make the STL containers have virtual destructors is because they saw the containers unfit for use is a polymorphic situation (not because they saw them unfit for public inheritance - there is a difference). Yet you try to question this, and manage to come up with these highly unlikely scenarios where people would go against the design intended by the STL writers and try to (wrongly) use STL containers in a polymorphic way. Well, bad design has its consequences. In this case, trying to use vector, a well-known class in the standard library, polymorphically even though it's clearly not meant to be used so, may lead to undefined behaviour. So why worry about the trouble people get into when they use a bad design?
-
Re: extending an STL container class Question
HighCommander, you have a point. However, when someone starts out with the STL, I think it's much safer not to recommend public derivation from containers. The risks are there, and if you don't know what you're doing exactly, you'll get into trouble. It's safer not to derive from them, so I think that it's entirely justified to warn against that.
The other thing to take into consideration is that if you want to derive from an STL container, you probably didn't understand the STL's philosophy correctly. Most cases of derivation are there to extend functionality and add new member functions. However the philosophy of the STL is that functions acting on the data should be decoupled from the containers. Take iterators and you'll have a much more powerful function than before. This is another point that I think is important to show to people who start out with the STL and which again speaks against public derivation.
-
Re: extending an STL container class Question
I'm in complete agreement with Yves here. The whole design of the STL is based around extending functionality via non-member functions and decoupling the algorithm from the container. Inheriting subverts that design, increases coupling and decreases the cohesion of your class. There is no need to inherit from a container just to add a function. You add a non-member that takes iterators, and you have a decoupled solution that is extendible and modifiable. It the OP later decides that list<> would have been a better choice of container, the decoupled solution is trivial to change, whilst the inherited solution can be a real pain.
There's also the advice given in Sutter and Alexandrescu's C++ coding guidelines: inherit to be reused, not to reuse. I don't have the book with me at the moment, but I'll check it out tomorrow and expand on that. But for now, they are saying, in essence, that, yes, public inheritance means IS-A, and further, that IS-A implies polymorphism.
-
Re: extending an STL container class Question
Quote:
Originally Posted by HighCommander4
vector<Event> and event_list, however, are classes that do not need to use polymorphism. The interfaces are the same: the OP hasn't redefined a single vector member function. He simply has added functionality in what I believe is the proper way to do so. I mean, why use private inheritance or containment in this case? Then you would need to write code that exposes each public vector member functions that the users of event_list need... what if all of vector's funtionality is needed by the users of event_list?
Anyone who would try to use a vector<Event>* to point to an event_list object would know that the base class is vector. And everyone should know that STL classes do not have virtual destructors. However, as I asserted above, no one would ever need to do such thing, so their knowing or not knowing whether vector has a virtual destructor is not a problem in the first place.
I made it public (originally) because of what you said, needing vector's functionality, which I use elsewhere. I didn`t need a virtual destructor. Or at least I was not aware of it if I did. (I didn`t even know what one was.) However, things did become messy for me, the inexperienced. Going to a function did make things cleaner for me.
-
Re: extending an STL container class Question
Quote:
Originally Posted by Yves M
However the philosophy of the STL is that functions acting on the data should be decoupled from the containers. Take iterators and you'll have a much more powerful function than before. This is another point that I think is important to show to people who start out with the STL and which again speaks against public derivation.
I understand now. Thanks
-
Re: extending an STL container class Question
Quote:
Originally Posted by HighCommander4
Public inheritance does not always mean polymoprhism. It simply means IS-A.
It is exact.
And if you write some non-polymorphic class and someone else want to use them polymorphically, there are two possibilities:
1) He wants to write some new classes whose base classes are a class you wrote (class B for example). In that case he must derive from class B, and explicitly declare a virtual destructor, and in that case, all classes derived from this new class, can be destroyed polymorphically with a pointer to this base class.
It is normal that someone who wants to use polymorphism must declare this polymorphism.
2) He wants to use polymorphically the already defined hierarchy of classes you wrote:
In that case, even if you defined destructors as polymorphic, he can't, because you have probably not defined the member functions he wants to use, as virtual.
So, if you think that all class hierarchy should be used polymorphically by everybody (and not only the new classes hierarchy somebody could want to write), you must define all functions as virtual, except very few functions whose polymorphism don't have meaning.
About the problem of this thread, i think that the vector should be defined as a class's member of event_list, except if all features of vectors are really needed, but in that case instead of deriving from vector, it is far better to write non-member functions using vector of events.
In fact, public inheritance should be seen as a 'IS-A' relation, but it is even better to see it as 'IS-A, and IS-more than', because if it is not more than the base class, it means that the derived class is written only to add new member functions, and in that case, why the base class cannot use this functions? Okay, in the case of an event vector, you can use the derived class everywhere in your project, and never use directly the base class, in that case the problem is solved, but it is less connectable with some other executables modules that could use the same vectors of events (what, in practice is not really a problem).
-
Re: extending an STL container class Question
Quote:
Originally Posted by Yves M
HighCommander, you have a point. However, when someone starts out with the STL, I think it's much safer not to recommend public derivation from containers. The risks are there, and if you don't know what you're doing exactly, you'll get into trouble. It's safer not to derive from them, so I think that it's entirely justified to warn against that.
The other thing to take into consideration is that if you want to derive from an STL container, you probably didn't understand the STL's philosophy correctly. Most cases of derivation are there to extend functionality and add new member functions. However the philosophy of the STL is that functions acting on the data should be decoupled from the containers. Take iterators and you'll have a much more powerful function than before. This is another point that I think is important to show to people who start out with the STL and which again speaks against public derivation.
Quote:
Originally Posted by Graham
I'm in complete agreement with Yves here. The whole design of the STL is based around extending functionality via non-member functions and decoupling the algorithm from the container. Inheriting subverts that design, increases coupling and decreases the cohesion of your class. There is no need to inherit from a container just to add a function. You add a non-member that takes iterators, and you have a decoupled solution that is extendible and modifiable. It the OP later decides that list<> would have been a better choice of container, the decoupled solution is trivial to change, whilst the inherited solution can be a real pain.
I see your point, but I have 2 concerns:
1) Is it really a good practice to pollute the global namespace with non-member functions, especially ones with a very common name like write(), when you could use a member function instead? That's not to mention the fact that write() is by no means an appropriate name for this function, since it only operates on Event objects. write_event() would be a more appropriate name for it. However, if it's a function that only operates on events, you need some ways of making sure the iterators passed to it yield an Event object when dereferenced. How do you check for that (assuming it's a template function, of course)? And if it's not a template function, then it would need to take vector<Event>::iterator arguments, and then you lose the flexibility of being able to use the function with difference containers, so what's the point? You would get the same coupling as with a member function, but with the added disadvantage of having polluted the global namespace.
2) Even if we accept a non-member function as an appropriate solution in this case, what about other cases where using a non-member function with iterators may not be a suitable solution? What if, say, you want to add functionality that modifies the way the container itself behaves? I stand by my view that in such cases, public inheritance could be appropriate.
-
Re: extending an STL container class Question
Quote:
Originally Posted by Graham
There's also the advice given in Sutter and Alexandrescu's C++ coding guidelines: inherit to be reused, not to reuse. I don't have the book with me at the moment, but I'll check it out tomorrow and expand on that. But for now, they are saying, in essence, that, yes, public inheritance means IS-A, and further, that IS-A implies polymorphism.
I would very much like to see these guidelines. If Herb Sutter believes that all public inheritance should mean polymorphism, why does the Standards Committee not make dynamic binding the only option for public inheritance? Or is that one of the revisions they are planning in the new Standard?
I, for one, am a firm believer in the fact that there are situations not suitable for polymorphism where public inheritance is an appropriate way of reusing code.
-
Re: extending an STL container class Question
Quote:
Originally Posted by HighCommander4
If the ISO Standards Committee would have believed that public inheritance without dynamic binding is risky to use, it would have made dynamic binding the default for public inheritance and left no other alternative. However, the use of dynamic binding has a conisderably high overhead and its overuse can affect a program's performance.
That's not what I'm saying. Public inheritance is useful for many things where virtual destructors (and thus polymorphism) are not needed. The many Boost CRTP classes (like for instance iterator_facade) are excellent examples of that. This is in fact a case where I disagree somewhat with the whole "public inheritance means IS-A" thing. I wouldn't say inheriting from boost::iterator_facade means my class IS-A (Or would it be IS-AN?) iterator_facade, and I certaily wouldn't exploit that polymorphically. The public inheritance in that case is an implemetation detail, and shouldn't even be known by the user.
What I am saying, is that if both the base, and derived classes are in the public and documented interface of the library/whatever you're developing (like in the case of vector and event_list), then the users should be able to use them polymorphically. (Yes, even delete through a base pointer.) If the hierarchy cannot be used polymorphically, you should hide the base class from the users and call it an implementation detail. (Which of course you cannot do with std::vector.)
Sigh.. Maybe it's just me...
Quote:
Originally Posted by HighCommander4
One thing I always liked about C++ as a language is that, unlike Java, you don't have to pay for what you don't need.
I quite agree.
Quote:
Originally Posted by HighCommander4
This is one of those cases where using dynamic binding is not necessary because no one in their right mind would ever use a pointer to a container, never mind assign a derived class object to it and then try to delete the derived class object through the pointer.
Well... Noone would do that knowing containers don't have virtual destructors. The problem is, not everybody knows that, so why open up for the possibility? Like others have pointed out, there are better ways of extending the functionality of containers. Inheriting from them is just not needed.
Quote:
Originally Posted by HighCommander4
The interfaces are the same: the OP hasn't redefined a single vector member function. He simply has added functionality in what I believe is the proper way to do so.
I disagree. The proper way to do so, would be through non-member functions.
Quote:
Originally Posted by HighCommander4
Anyone who would try to use a vector<Event>* to point to an event_list object would know that the base class is vector. And everyone should know that STL classes do not have virtual destructors.
In this specific case most people would know that, yes. But that doesn't make it okay. I don't think this should be required information for people simply using your vector inherited class.
In the more general case, if I made a class with no virtual destructor (thereby signalling "don't inherit from this one"), and you did anyway, other programmers would not know that, and they would probably assume polymorphism is safe. (I certainly would) Why should people have to check my base class for a virtual destructor before they use it polymorphically?
Quote:
Originally Posted by HighCommander4
However, as I asserted above, no one would ever need to do such thing, so their knowing or not knowing whether vector has a virtual destructor is not a problem in the first place.
How do you know that? Someone might need to do that... You just don't know.
Quote:
Originally Posted by HighCommander4
I think the central idea here is that there is a difference between public inheritance and polymoprhism. Public inheritance does not always mean polymoprhism. It simply means IS-A.
Yes.. And in my opinion IS-A means polymorphism, but public inheritance does not *always* mean IS-A (Sorry Graham! :D) That's where we disagree I guess.
Quote:
Originally Posted by HighCommander4
The reason the STL writers did not make the STL containers have virtual destructors is because they saw the containers unfit for use is a polymorphic situation (not because they saw them unfit for public inheritance - there is a difference).
Are you sure that is really what they meant? I would really like to see a quote to that effect if they do. As far as I have been able to gather, no virtual destructor means "do not inherit".
Quote:
Originally Posted by HighCommander4
Yet you try to question this, and manage to come up with these highly unlikely scenarios where people would go against the design intended by the STL writers and try to (wrongly) use STL containers in a polymorphic way. Well, bad design has its consequences. In this case, trying to use vector, a well-known class in the standard library, polymorphically even though it's clearly not meant to be used so, may lead to undefined behaviour. So why worry about the trouble people get into when they use a bad design?
The point is that you are shifting a big resposibillity over to the users of your class. If everyone did that we would have to check any base classes for virtual destructors every single time we use them polymorphically. Do you do that?
If you instead just say IS-A means polymorphism and that public inheritance only means IS-A if the base class is available to the users, then they just don't have to do this anymore. It takes away quite a few possibilities for errors made by programmers. (Always a good thing)
Phew.. I don't know. Am I making myself a little clearer now? :)
-
Re: extending an STL container class Question
Quote:
Originally Posted by HighCommander4
Is it really a good practice to pollute the global namespace with non-member functions, especially ones with a very common name like write(), when you could use a member function instead?
You could always make a namespace and put the fuction in it. Or just name it better.. write_event, like you said yourself.
Quote:
Originally Posted by HighCommander4
However, if it's a function that only operates on events, you need some ways of making sure the iterators passed to it yield an Event object when dereferenced. How do you check for that (assuming it's a template function, of course)?
Boost has an excellent solution for that. Static assertions. You could just assert that std::iterator_traits<T>::value_type equals Event, and trying to pass anything other than that would fail at compile time. This would work no matter what iterator type T is...
-
Re: extending an STL container class Question
Quote:
Originally Posted by wien
Are you sure that is really what they meant? I would really like to see a quote to that effect if they do. As far as I have been able to gather, no virtual destructor means "do not inherit".
It's a simple logical conclusion. Publically inheriting from a class with no virtual destructor does not result in undefined behaviour. Using a class hierarchy where the base class does not have a virtual destructor, polymorphically, may result in undefined behaviour. Hence, having no virtual destructor is a signal that it is unsafe to use a hierarchy with this class a base class, polymorphically, *not* that it's unsafe to publically derive from this class.
What also follows from this conclusion is that if someone tries to use a class hierarchy where the base class does not have a virtual destructor, polymorphically, the consequences of this are entirely their responsibility, because they are the ones who are not using the hierarchy the way it is intended to be used, not whoever wrote the derived class(es).
Quote:
Originally Posted by wien
The point is that you are shifting a big resposibillity over to the users of your class. If everyone did that we would have to check any base classes for virtual destructors every single time we use them polymorphically. Do you do that?
If you instead just say IS-A means polymorphism and that public inheritance only means IS-A if the base class is available to the users, then they just don't have to do this anymore. It takes away quite a few possibilities for errors made by programmers. (Always a good thing)
wien, I respect your viewpoint on this issue but I still think that not every publicly available class hierarchy needs to be used polymorphically. Some do, some don't. In either case, the intentions of the code writers should be well documented, so that the user does not have to infer these intentions by looking at destructors etc. If a user is unable to tell whether or not a class hierarchy should be used polymorphically, it's a sign of poor documentation, not of poor design. I completely agree with you that no one should have to take a look at whether or not a base class has virtual destructors to be able to determine whether or not to use the hierarchy polymorphically. But having to enable dynamic binding, thus incurring considerable run-time oberhead, for every single publically available class hierarchy even if it is one that does need polymorphism (for example, vector<Event> and event_list), just because there are some people who do not bother to read the documentation and who blindly use others' code for whatever need they see fit without consideration of the code writers' intentions, is quite unreasonable.
-
Re: extending an STL container class Question
I present this purely for information:
C++ Coding Standards, Sutter and Alexandrescu:
Quote:
35. Avoid inheriting from classes that were not designed to be base classes.
[...]Using a standalone class as a base is a serious design error and should be avoided. To add behavior, prefer to add nonmember functions instead of member functions (see item 44). To add state, prefer composition instead of inheritance (see item 34). Avoid inheriting from concrete base classes.
[...]
Quote:
37. Public inheritance is substitutability. Inherit, not to reuse, but to be reused.
[...]Don't inherit publically to reuse code (that exists in the base class); inherit publically in order to be reused (by existing code that already uses base objects polymorphically).
[...]
Quote:
44. Prefer writing nonmember nonfriend functions.
[...]Where possible, prefer making functions nonmember nonfriends.
[...]Nonmember nonfriends improve encapsulation by minimizing dependencies: the body of the function cannot come to depend on the nonpublic members of the class.[...]
-
Re: extending an STL container class Question
Quote:
Originally Posted by wien
Boost has an excellent solution for that. Static assertions. You could just assert that std::iterator_traits<T>::value_type equals Event, and trying to pass anything other than that would fail at compile time. This would work no matter what iterator type T is...
For those interested:
boost also has something called concepts that is used for example by boost::graph.
Some algorithms in boost::graph works for example only on bi-directional graphs. boost::graph is heavily templatized and in order to both make sure that the graph that is passed to the algorithm is a bi-directional graph and to provide user-friendly error messages (compile-time) if it's not boost::graph uses concepts.
Concepts basically are more high-level static assertions.
http://www.boost.org/libs/concept_ch...cept_check.htm
-
Re: extending an STL container class Question
Actually your write_event looks like it would best suited as a function-object. Then you could call
Code:
std::for_each( v.begin(), v.end(), MyEventWriter( my_outstream ) );
Alternatively if you want to create MyEventWriter so you still have a copy of it after the std::for_each you can create it first, thus:
Code:
MyEventWriter evwr( myOutStream );
std::for_each( v.begin(), v.end(), evwr );
If you don't want to use operator() in evwr but a named member function instead, you can use bind.
-
Re: extending an STL container class Question
Quote:
Originally Posted by HighCommander4
What also follows from this conclusion is that if someone tries to use a class hierarchy where the base class does not have a virtual destructor, polymorphically, the consequences of this are entirely their responsibility, because they are the ones who are not using the hierarchy the way it is intended to be used, not whoever wrote the derived class(es).
I would not agree to this. If I am constructing something I should try as much as possible that people dont mess up with my product. Taking a simple example - getters and setters - if I dont wish to provide 'set' access to someone I won't provide a setter method and make my data member private. Its not that I would make it public and in the document I would write that this member should not be set !!! This does not make any sense.
By the way, What I would say here is that if a particular class is not meant for inheritance (or does not have virtual destructors) we should have a mechanism to not allow their derivation in the first place. And if we are allowing public inheritance there should be no loop-holes about usage, be it polymorphic .. be it non-polymorphic. But again I would agree to HighCommander's quote - "In C++ you don't have to pay for what you don't need." Now, this again asks for allowing base classes publicly inheritable without virtual destructors.
Quote:
Originally Posted by HighCommander4
...just because there are some people who do not bother to read the documentation and who blindly use others' code for whatever need they see fit without consideration of the code writers' intentions, is quite unreasonable.
In my opinion, Agreed!!! Completely. How could you use others' code without reading the documentation. But I would also add that its not always practical. You might be lacking documentation. You might even get enhancement work in which you dont have them in hand. There can be various such cases in the real world scenario. In some places, where processess are not implemented people dont even do any documentation!!!
I guess there's no particular conculsion to this. This basically depends on the possibilities of usages of ur code. The degree to which they would be re-used. The life-time of the software, in a general sense.
-
Re: extending an STL container class Question
Quote:
Originally Posted by HighCommander4
But having to enable dynamic binding, thus incurring considerable run-time oberhead, for every single publically available class hierarchy even if it is one that does need polymorphism (for example, vector<Event> and event_list), just because there are some people who do not bother to read the documentation and who blindly use others' code for whatever need they see fit without consideration of the code writers' intentions, is quite unreasonable.
This obviously has become quite a passionate discussion. I think you need to take a step back and think for a moment: what do you gain by publically inheriting from vector in the first place? OK, so it seems more "natural" to inherit from the container, since an event_list IS-A vector. But look at what you lose:
1. Genericity of underlying container type.
2. Clarity of your intentions and the underlying implementation.
These things, and especially #2, cannot be ignored when you're working at a "real" job where intuitive code re-use means less programming time, which means shorter schedule and less cost, which is good for everyone. I wholeheartedly agree with wien in this case, as you can't be sure that someone will read your documentation, or if they will even have access to it. It doesn't make sense to buck the trend of conventional C++ design principles to make something that seems more natural to you, especially when there is an alternative that will prevent confusion and also grant you the ability to change containers in the future. Write code to be reused, not just by you, but by any of your colleagues; you'll be glad you did.
-
Re: extending an STL container class Question
You can write a class that cannot be inherited from by giving it private constructors and a friend or static member to construct one (which would also force heap construction, because the copy-constructor would also have to be private).
In Java they have final but then Java also enforces heap-construction.
Of course, you could get your constructor to return you a reference-counted pointer rather than a raw pointer so you won't have to worry about calling delete. And it wouldn't have to necessarily be a standard reference-counted pointer, but could be a custom-made one.
But now you'll have problems, thus:
- Does it use COW or share the data when copied?
- Who is to stop someone deriving from your reference-counted pointer?
-
Re: extending an STL container class Question
Quote:
Originally Posted by wien
The public inheritance in that case is an implemetation detail, and shouldn't even be known by the user.
No, public inheritance always means "IS-A", and it is known by the user, because the user can use the derived object like a base object. That means that the user can use all the member functions of the vector.
private inheritance can be an implementation detail, but public inheritance, is never an implementation detail.
And, private inheritance itself is very useful in some cases, but can generally (not always) be replaced by composition (i mean, putting a field in the class's declaration).
-
Re: extending an STL container class Question
About virtual destructors, you must know that it is really useless to declare virtual destructors in classes that have no virtual functions.
Let me explain.
Consider for example std::vector<SomeType>.
Somebody wants to derive many classes from std::vector<SomeType> and to create a whole polymorphic complex hierarchy.
There is no problem if he uses polymorphically all is new hierarchy, because he defines a virtual destructor (and some other virtual functions, of course, because if that was not the case he could not use polymorphically the class except for destroying it) in a base class derived from std::vector<SomeType>.
But, now he wants to use a pointer that may be a pointer to std::vector<SomeType> or be a pointer to a derived object. That is, he wants to use polymorphically, not only its own new hierarchy, but also the old hierarchy. In that case, you could think that the std::vector implementation of the STL should declare its destructor as virtual.
But it has no meaning! Because std::vector does not contain any virtual member function, what means that the programmer cannot use polymorphically a pointer to std::vector<SomeType>. Even more, if he calls operator[] (for example) on a pointer to std::vector<SomeType>, it will inlines the call.
There is a general rule : never use virtual destructor in a class that don't contain any virtual members.
So, you may think. Why the std::vector implementation of the STL has not virtual members for all his members.
There are two responses:
1) Performance will be very, very bad.
2) A vector is not designed to be derived polymorphically.
Who wants to derive from std::vector, and redefine the operator[] and use polymorphically pointers to the old std::vector, and pointers to the new derived class? At least if there were an abstract base class containing the vector operations, it would have a meaning, but since all the implementation of std::vector is contained in the vector, you are constrained to loose all the memory space of the vector implementation if you want to derive from it and use another implementation.
A general rule:
Except if the class is specifically designed to be polymorphic, and be derived by everybody (for example std::basic_streambuf), you must not use polymorphically the class hierarchy of classes that are not already polymorphic, because... You cannot... Not only the destructor may not be virtual, but also there is probably no virtual functions that you could use polymorphically, and polymorphism needs virtual functions. And everybody knows that making a function virtual don't make it virtual in the parent classes, but only in the derived classes.
But, you can always derive from a class, and create a new class hierarchy derived from this class, and in that case you can use polymorphically all your newly-defined hierarchy.
And if you think that every class should be fully polymorphic, for every member functions, then you should program with Java and not C++.
-
Re: extending an STL container class Question
Superkoko has convinced me completely. I conclude that vectors could be inherited publicly and non-polymorphically. If some one does try some polymorphic usage, that would be conceptually wrong !! And the problems he would get into are completely his responsibilities. However, in this particular case I conclude that we would be losing some flexibility when the need arises for the change of the container. Yves, Graham and wien might not agree :) and it would be great to get some comments from them about the things pointed out by SuperKoko.
By the way..great thread !!! Cheers to all,
Exterminator.
-
Re: extending an STL container class Question
OK. Let me try.
Point 1. I strongly disagree that event_list IS-A vector<Event>. It is implemented-in-terms-of a vector. Look at it this way: if you replaced vector<> by list<> or deque<>, would you expect to be unable to write an equivalent program? I suggest that it would be very simple to replace the vector by a deque, and so and event_list is a container of events, but not uniquely a vector. So, IS-A doesn't apply, therefore public inheritance doesn't apply.
In other words, you are inherting to reuse base code, which brings us to...
Point 2. Some more from Sutter and Alexandrescu:
Quote:
The purpose of public inheritance is not for the derived class to reuse base class code to implement itself in terms of the base class's code. Such an implemented-in-terms-of relationship can be entirely proper, but should be modeled by composition -- or, in special cases only, by nonpublic inheritance [...]
A new derived class is a new special case of an existing general abstraction.[...]
New requirements should naturally be met by new code; new requirements should not cause rework on existing code.
(All the above from item 37 of C++ Coding Standards).
I realise that point 2 is an argument from authority, but all I would say is that this particular authority has always worked for me in the past, and I have found that following Sutters's advice on matters like this leads to much less bugfixing and fewer problems down the line.
-
Re: extending an STL container class Question
Quote:
Originally Posted by Graham
Point 2. Some more from Sutter and Alexandrescu:
Quote:
The purpose of public inheritance is not for the derived class to reuse base class code to implement itself in terms of the base class's code. Such an implemented-in-terms-of relationship can be entirely proper, but should be modeled by composition -- or, in special cases only, by nonpublic inheritance [...]
A new derived class is a new special case of an existing general abstraction.[...]
New requirements should naturally be met by new code; new requirements should not cause rework on existing code.
(All the above from item 37 of
C++ Coding Standards).
I realise that point 2 is an argument from authority, but all I would say is that this particular authority has always worked for me in the past, and I have found that following Sutters's advice on matters like this leads to much less bugfixing and fewer problems down the line.
First of all, I don't understand how deriving from vector leads to circumstances where new requirements cause rework on existing code... could you elaborate on that?
Second, what if the users of event_list expected all of vector's functionality to come with it (operator[], push_back(), insert(), erase(), size() and so on), yet we were to follow Sutter's advice and use containment or nonpublic inheritance... we would have to write 20 or so one-line functions in the event_list class that do nothing else but call the vector equivalents... Isn't one of the major features of object-oriented programming to reuse code as much as possibl? Yet Sutter seems to suggest that re-writing all that code is appropriate in this case?
You talk about making code less bug-prone...what about all the possibilities for introducing new bugs in the process of rewriting so much code for no reason? In my opinion the situation screams for public inheritance...
I do realize that other solutions (like using non-member functions) may be viable in this case, but definitely not containment or private inheritance.
-
Re: extending an STL container class Question
If you look back, my original suggestion (or, rather, jlou's, which I agreed with) - and, indeed, one the first extracts I posted from Sutter - was to implement the new functionality as a standalone (nonmember) function. That has been my postion throughout as the best solution. I have never suggested that the OP's problem should be solved by making a new class, whether one that derives from vector, nor one that aggregates vector. The new code is one new function. No rewriting, no reams of pass-through functions: one new function that meshes well with the spirit and philosophy of STL containers.
Of course you run the risk of introducing bugs as you've suggested. That's why I have not advocated doing it that way. But I firmly disagree that it screams for public inheritance. It does not, for reasons that I outlined in the post you've just quoted: the situation does not model IS-A, and for the reasons given by Sutter and Alexandrescu. That posting was an argument against public inheritance, not for containment.
-
Re: extending an STL container class Question
There is a "cheat" way to bring in all your functionality of vector without having to derive from it.
Overload operator-> (if it isn't already used).
So
Code:
class EventList
{
private:
std::vector< Event > m_events;
public:
std::vector< Event > * operator->() { return &m_events; }
const std::vector< Event > * operator->() const { return &m_events; }
};
Ok, operator[] is now messy so overload that too. But the rest like push_back you get automatically.
Code:
class EventList
{
public:
typedef std::vector< Event > vector_type;
typedef vector_type::size_type size_type;
private:
vector_type m_events;
public:
vector_type * operator->() { return &m_events; }
const vector_type * operator->() const { return &m_events; }
Event& operator[] ( size_type i ) { return m_events[i]; }
const Event & operator[] ( size_type i ) const { return m_events[i]; }
};
-
Re: extending an STL container class Question
Quote:
Originally Posted by exterminator
and it would be great to get some comments from them about the things pointed out by SuperKoko.
Ask, and ye shall recieve. :)
Quote:
Originally Posted by SuperKoko
No, public inheritance always means "IS-A", and it is known by the user, because the user can use the derived object like a base object. That means that the user can use all the member functions of the vector.
private inheritance can be an implementation detail, but public inheritance, is never an implementation detail.
I was talking about the CRTP (Curiously Recurring Template Pattern) classes in boost, not the event_list/vector relationship. They are used to implement a lot of member-function s "on behalf" of classes inheriting from them. boost::iterator_facade for example implements all the operators an STL iterator needs, based on four functions (increment, decrement, equal and dereference) implemented in the derived class. This is done without any kind of polymorphism (run-time that is), and the CRTP classes will never be used in client code, and certainly not to achieve any kind of polymorphism.
In those cases I would consider public inheritance an implementation detail, and thus, not a case of IS-A.
Quote:
Originally Posted by SuperKoko
About virtual destructors, you must know that it is really useless to declare virtual destructors in classes that have no virtual functions.
Let me explain.<snip>
Well, yes. But to make a class meant to be used as a base class, without any virtual functions is somewhat of a design-flaw in my opinion. What would then be the point in deriving from it? Code reuse? In that case I refer to the quotes Graham posted.
Furthermore, nothing is preventing users from deleting a derived object through a pointer to the virtual-less base, so that could still lead to problems. (I admit the chance of that happening is very slim with no polymorphism possible, but as I have said before, that doesn't make it okay. People do strange things sometimes, and it's always better to try to limit their possibilities to do so.)
Quote:
Originally Posted by SuperKoko
So, you may think. Why the std::vector implementation of the STL has not virtual members for all his members.
There are two responses:
1) Performance will be very, very bad.
2) A vector is not designed to be derived polymorphically.
Who wants to derive from std::vector, and redefine the operator[] and use polymorphically pointers to the old std::vector, and pointers to the new derived class? At least if there were an abstract base class containing the vector operations, it would have a meaning, but since all the implementation of std::vector is contained in the vector, you are constrained to loose all the memory space of the vector implementation if you want to derive from it and use another implementation.
The primary reason STL containers don't have virtual members, is that their design are not really based on OOP at all, but rather generic programming. The Container/Iterator/Algorithm concepts introduced in the STL give us a much better way of extending it's functionality, rendering inheritance more or less useless for that purpose.
-
Re: extending an STL container class Question
Quote:
Originally Posted by wien
But to make a class meant to be used as a base class, without any virtual functions is somewhat of a design-flaw in my opinion. What would then be the point in deriving from it? Code reuse? In that case I refer to the quotes Graham posted.
But which functions must be virtual?
Consider the class complex which represents a complex number.
Which functions should be virtual?
To allow the best flexibility the solution is to define an abstract base class (not doing so don't allow to change the numbers representation) containing these virtual functions:
real, imag, conj, norm, arg, pow, operator+(), operator-(). abs, cos, sin, exp, log, log10, tan, atan, tanh, sinh, cosh, acos, asin.
The binary operators should also be defined.
Many of these functions can have a default implementation that use one of the base accessors : real, imag, arg, norm and conj.
For binary operators, that is more complex, because even if we let them pure in the base class, in the derived classes we must choose to return a representation depending on the first parameter of the operator (that is the this pointer), but not depending on the second parameter.
Now, we want to implement 4 derived classes.
ScalarFloatComplex (with a representation containing a float real part, and a float imaginary part)
PolarFloatComplex (containing floats for argument and magnitude).
ScalarDoubleComplex
PolarDoubleComplex
Note that the accessors of the base class, are all returning double.
Now the real problem comes in the implementation of binary operators.
We want that a ScalarFloatComplex+a ScalarDoubleComplex returns a ScalarDoubleComplex and not a ScalarFloatComplex.
But in the ScalarFloatComplex::operator*(complex &x) we cannot know if x uses floats or doubles without doing RTTI (but doing RTTI breaks the genericity), so we choose to return a ScalarDoubleComplex.
And, we can just do an implementation that use the complex::real and complex::imag virtual member functions.
In fact, if you look at all operators of all complex numbers you will see that there will be many conversions from float to double and polar to scalar representation or scalar to polar representation. And writing x+y is not the same as writing y+x if x and y are not using the same representation.
In fact, it is preferable to use only one representation for all complex numbers (or at least don't mix representations).
But, now consider extending the standard complex class.
Why can we extends this class?
Not to modify the representation, because it is already defined!
But, we can add new member functions, and no fields. But for that we should not derive, but just add non-member functions.
We can derive to add caching informations.
That is a true concrete application of derivation.
For example, we want to cache the magnitude of the complex number, because we write an application that needs many call to the norm functions.
The ComplexWithMagnitudeCached class is just deriving from complex, and redefines the norm class, and adds a double magnitude; field whose value is initiated to +NAN.
So, we can think that the best is to declare all functions as being virtual in the complex class, because derived classes can derive from them.
But, you don't need that!
You can redefine norm in the derived class without needing this function be virtual.
And it is really absolutely sure that you will not use this class polymorphically with a pointer that can points either to complex or to ComplexWithMagnitudeCached. You will use ComplexWithMagnitudeCached everywhere needed, and complex everywhere you don't need that the norm be computed fastly, and write a conversion operator from complex to ComplexWithMagnitudeCached, but you will never have polymorphic pointers. Moreover if we manipulate polymorphic pointers we cannot know if the norm function executes fastly or not. It is far better to don't use polymorphism here.
Moreover a ComplexWithMagnitudeCached can be used perfectly with functions (of a foreign library for example) that use pointers or references (or values) to the complex class.
The only thing you must know, is that the norm function of the base class will be called instead of the optimized function of the derived class. What is not really a problem, because this function do its work correctly. Yes, the optimization will not be used, but if the library do much computing that really needs to compute fastly magnitudes, be sure that there is no problem, because this library probably uses its own optimized representation in internal computings (if it is an optimal library).
Moreover in many situations we use complex values (and not pointers nor references). And in that case polymorphism is totally useless.
And polymorphism is really not cheap for complex numbers, because:
1) It needs a Vitual Table pointer which uses 4 bytes, and for alignment reasons the compiler (or the programmer) will align on 8 bytes boundary the structure, so it uses 24 bytes instead of 16 bytes.
2) since most of the frequently used functions of complex are very simple, making them virtual reduce by a not-negligible factor the speed (even if this factor is not exorbitant).
The memory space factor is probably the more important.
The complex class can be derived, but is cannot be polymorphically used, what does not means that its derived classes can't be polymorphically used.
If you need to use polymorphically a whole complex number hierarchy, you can. You just need to derive the base class of your hierarchy from complex, and to redefines as virtual, exactly all the functions you need to be virtual (including very probably the destructor).
So, by not putting any virtual functions in the complex class, the flexibility is maximized. Because you can choose, when you derive from it, to not define any virtual function (the most probable), or to defines exactly the functions you need as virtual (probably totally new functions, and probably not already existing functions in the complex class).
You must know that if an exisiting hierarchy is not polymorphic that don't means that all derived classes cannot be polymorphic, and that don't means also that the already existing hierarchy is bad. It just means that the already existing hierarchy has no need to be polymorphically used. Because, for example, all functions don't need to be redefined polymorphically. They are defined optimally and do exactly the job they need to do.
-
Re: extending an STL container class Question
An other application of derivation from complex, is defining the PureImaginary deriving from complex, with a one-argument constructor (the imaginary part of the complex number), and some new member functions (i don't know which functions we can put, but there are probably some member functions specific to pure imaginary numbers).
We don't need polymorphism.
We can redefine a few functions like norm for example, to have a faster function.
And, if we needed some optimized computing with pure imaginary numbers, by defining a non-member function:
complex &complex::operator*(const PureImaginary &);
And some few other functions.
And where we have a pointer to a complex number without knowing if it is a pointer to complex or PureImaginary, the base functions will be used, producing a little-less-performant code, but a code probably far more performant that a code using virtual functions. And we will probably not use many pointers to complex, instead we will probably use some complex values, or non-polymorphic (i means a pointer to complex that cannot be a pointer to PureImaginary, or a pointer to PureImaginary) references or pointers.
And, if there is one functions whose polymorphism is really needed because it is executed 10000 times faster with PureImaginary and there is code where there are pure imaginary and complex numbers mixed.
We can derive from complex, and this virtual function (and a virtual destructor), and derive PureImaginary from this new base class (named NewComplex for example), and not use the complex class at all in all the project (except for conversions from NewComplex to complex for calling already defined functions that works with complex in foreign libraries for example, but you can see that adding a virtual function don't limit the usage of NewComplex. NewComplex IS-A complex, and can be used as a complex).
But, probably a better thing to do (not the best solution), is deriving from complex just one class named ComplexOptimizedForPureImaginaryOperations for example, and redefine the function we optimize, and only this one, and in this function we just compare the real part of the complex to zero. And if it is zero, we use the optimized code, and invoke the old function if it is non-zero.
Now, we just use the ComplexOptimizedForPureImaginaryOperations class in all our project.
And this solution works if new Imaginary numbers appear (by doing operations on non-pure-imaginary complex numbers that produces a pure imaginary number)
In fact, the best solution is probably to define a non-member function named Fast[The operation name] that takes a complex argument and does the fast processing needed after comparing the real part to zero.
It follows the rule : only derive if the new class IS-A base class, and IS-MORE-THAN the base class.
I hope that these examples will show you that, even if polymorphism is one of the best thing ever, it is not needed everywhere, and must not be declared where really not needed.
-
Re: extending an STL container class Question
Quote:
Originally Posted by NMTop40
There is a "cheat" way to bring in all your functionality of vector without having to derive from it.
It is cute, but even if the derivation term don't apply, all what is implied by public derivation is implied.
I think that you are perfectly conscious of that.
:) :D
-
Re: extending an STL container class Question
Quote:
Originally Posted by wien
Ask, and ye shall recieve. :)I was talking about the CRTP (Curiously Recurring Template Pattern) classes in boost, not the event_list/vector relationship. They are used to implement a lot of member-function s "on behalf" of classes inheriting from them. boost::iterator_facade for example implements all the operators an STL iterator needs, based on four functions (increment, decrement, equal and dereference) implemented in the derived class. This is done without any kind of polymorphism (run-time that is), and the CRTP classes will never be used in client code, and certainly not to achieve any kind of polymorphism.
In those cases I would consider public inheritance an implementation detail,
Yes, it is an implementation detail, since it is possible to write a STL that don't derive iterators from iterator_facade, but the "IS-A" rule is respected.
The name iterator_facade is not well choosed. iterator is a better term to name this class. But because the name iterator is reserved to be typedefed in container classes, it is not used.
And you probably understand that vector<>::iterator IS-AN iterator, that is an object that basically accepts operator++ operator= and operator*
But, programmers can be aware of not of the existence of the base class iterator.
In the same order of idea : Humans are primates. And everybody know the member functions of primates, but there are (not much) people that don't know that humans are primates, but these people know all the member functions of primates, and just think that these member functions are part of the declaration of humans.
It is a classification. And in a classification you can choose to simplify the hierarchy, producing a less flexible hierarchy, and producing some duplicate declarations. But, even if more than one classification can be correct, and choosing one classification and not another can be arbitrary. the IS-A rule is never violated in a given classification.
To be more precise there are two possible rules:
public inheritance can means "IS-A" or "IS-AN"
:D
-
Re: extending an STL container class Question
iterators are designed to be copyable and therefore there is a problem if they derive from a base class because you may get a slicing problem.
The way to make a generic iterator copyable is:
- Have an abstract base class behind it that performs functionality such as increment(), decrement(), dereference() and isequal()
- Your iterator needs a pointer to the generic iterator and performs all its functionality through it. Thus your iterator is a concrete class.
- For RAII the pointer must be reference-counted and must also be copy-on-write.
- Most of the implementation is easy, except isequal(). However if you add in another function isend() then you can test for equality by seeing if &(dereference()) == &(rhs.dereference() ) when neither of them are end(). (This is not guaranteed, however).
Code:
if ( isend() )
{
return rhs.isend();
}
else
{
return ! rhs.isend() &&
&(dereference()) == &(rhs.dereference() );
}
This is not guaranteed to work either, especially for const iterators.