Related
While going through a very good book on templates in C++, I came across an explanation on alternative to templates that I don't understand:
These are bad alternatives to templates in C++
You can write general code for a common base type such as Object or void*.
Reason : If you write general code for a common base class you
lose the benefit of type checking. In addition, classes may be
required to be derived from special base classes, which makes it more
difficult to maintain your code.
Can someone explain this with a code example?
It's not the concept of a common base type that's bad. It's the use of a "Object class" that everything has to be derived from, or worse, writing code that takes void* and then, making assumptions about what the pointer points to, typecasts to a pointer to some other type and hopes for the best. This is best exemplified with containers.
The right way to implement container methods is with templates. For example:
template<typename T> void List<T>::append(const T& obj);
Object class
In the case of an Object base class, what that means is that anything you put in the container must be derived from Object, because all the container methods use Object* for the data in said container. So you get methods like this:
void List::append(Object* obj);
Two bad things here: First, that Object class has to get dragged around with your container wherever you go. Second, it's a horribly generic name and will probably conflict with an Object class from some other library.
Also, your container can never contain types that aren't Object derived directly, including primitive types like int and standard types like std::string. You'd have to "wrap" those types in Object subclasses, and then you'd have to spend time with code to extract the values from those wrapper objects, etc. It's a pain in the rear that you don't need.
void*
So you might think you could use the generic pointer void* instead:
void List::append(void* obj);
But when you do that, there are many things the container may need to do that it can't, because it has no idea what that void* points to:
It can't copy data objects.
It can't compare data objects.
It can't delete data objects.
and so on. (You can avoid these problems in the Object* case with virtual methods, for example declaring something like:
virtual ~Object() {}
virtual Object* clone() const;
virtual int cmp(const Object* rhs) const;
where these methods must be overridden by all subclasses, in your Object base class. But now your Object class isn't very lightweight.)
In both cases, you'd be far better off using a templated type for your container's data type. If you are worried about code bloat and have code in your container that doesn't care about the data type (because it doesn't bother with the data, such as when counting contained elements), you can put that code in a base class and have your templated container class derive from it. But most of the time nobody really cares about this "bloat" because it's much smaller than your available memory.
Type checking discarded
If you write general code for a common base class you lose the benefit of type checking.
Since you've typecast to Object* or void*, type checking went out the window for the most part. (Note: this is somewhat dated, since in some cases you can use Runtime Type Identification (RTTI) and the dynamic_cast operation to perform type checking after the fact, to make sure the object you pulled out of the container is the type you expect. But all the above-mentioned limits still apply, since the container still doesn't know what it's containing.)
The old qsort function used void* pointers for the start of the data and for the parameters to the comparison function. You could easily try to sort an array of double with a comparison function that compared int and wind up with a real mess.
Ok, this is really difficult to confess, but I do have a strong temptation at the moment to inherit from std::vector.
I need about 10 customized algorithms for vector and I want them to be directly members of the vector. But naturally I want also to have the rest of std::vector's interface. Well, my first idea, as a law-abiding citizen, was to have an std::vector member in MyVector class. But then I would have to manually reprovide all of the std::vector's interface. Too much to type. Next, I thought about private inheritance, so that instead of reproviding methods I would write a bunch of using std::vector::member's in the public section. This is tedious too actually.
And here I am, I really do think that I can simply inherit publicly from std::vector, but provide a warning in the documentation that this class should not be used polymorphically. I think most developers are competent enough to understand that this shouldn't be used polymorphically anyway.
Is my decision absolutely unjustifiable? If so, why? Can you provide an alternative which would have the additional members actually members but would not involve retyping all of vector's interface? I doubt it, but if you can, I'll just be happy.
Also, apart from the fact that some idiot can write something like
std::vector<int>* p = new MyVector
is there any other realistic peril in using MyVector? By saying realistic I discard things like imagine a function which takes a pointer to vector ...
Well, I've stated my case. I have sinned. Now it's up to you to forgive me or not :)
Actually, there is nothing wrong with public inheritance of std::vector. If you need this, just do that.
I would suggest doing that only if it is really necessary. Only if you can't do what you want with free functions (e.g. should keep some state).
The problem is that MyVector is a new entity. It means a new C++ developer should know what the hell it is before using it. What's the difference between std::vector and MyVector? Which one is better to use here and there? What if I need to move std::vector to MyVector? May I just use swap() or not?
Do not produce new entities just to make something to look better. These entities (especially, such common) aren't going to live in vacuum. They will live in mixed environment with constantly increased entropy.
The whole STL was designed in such way that algorithms and containers are separate.
This led to a concept of different types of iterators: const iterators, random access iterators, etc.
Therefore I recommend you to accept this convention and design your algorithms in such way that they won't care about what is the container they're working on - and they would only require a specific type of iterator which they'd need to perform their operations.
Also, let me redirect you to some good remarks by Jeff Attwood.
The main reason for not inheriting from std::vector publicly is an absence of a virtual destructor that effectively prevents you from polymorphic use of descendants. In particular, you are not allowed to delete a std::vector<T>* that actually points at a derived object (even if the derived class adds no members), yet the compiler generally can't warn you about it.
Private inheritance is allowed under these conditions. I therefore recommend using private inheritance and forwarding required methods from the parent as shown below.
class AdVector: private std::vector<double>
{
typedef double T;
typedef std::vector<double> vector;
public:
using vector::push_back;
using vector::operator[];
using vector::begin;
using vector::end;
AdVector operator*(const AdVector & ) const;
AdVector operator+(const AdVector & ) const;
AdVector();
virtual ~AdVector();
};
You should first consider refactoring your algorithms to abstract the type of container they are operating on and leave them as free templated functions, as pointed out by majority of answerers. This is usually done by making an algorithm accept a pair of iterators instead of container as arguments.
If you're considering this, you've clearly already slain the language pedants in your office. With them out of the way, why not just do
struct MyVector
{
std::vector<Thingy> v; // public!
void func1( ... ) ; // and so on
}
That will sidestep all the possible blunders that might come out of accidentally upcasting your MyVector class, and you can still access all the vector ops just by adding a little .v .
What are you hoping to accomplish? Just providing some functionality?
The C++ idiomatic way to do this is to just write some free functions that implement the functionality. Chances are you don't really require a std::vector, specifically for the functionality you're implementing, which means you're actually losing out on reusability by trying to inherit from std::vector.
I would strongly advise you to look at the standard library and headers, and meditate on how they work.
I think very few rules should be followed blindly 100% of the time. It sounds like you've given it quite a lot of thought, and are convinced that this is the way to go. So -- unless someone comes up with good specific reasons not to do this -- I think you should go ahead with your plan.
There is no reason to inherit from std::vector unless one wants to make a class that works differently than std::vector, because it handles in its own way the hidden details of std::vector's definition, or unless one has ideological reasons to use the objects of such class in place of std::vector's ones. However, the creators of the standard on C++ did not provide std::vector with any interface (in the form of protected members) that such inherited class could take advantage of in order to improve the vector in a specific way. Indeed, they had no way to think of any specific aspect that might need extension or fine-tune additional implementation, so they did not need to think of providing any such interface for any purpose.
The reasons for the second option can be only ideological, because std::vectors are not polymorphic, and otherwise there is no difference whether you expose std::vector's public interface via public inheritance or via public membership. (Suppose you need to keep some state in your object so you cannot get away with free functions). On a less sound note and from the ideological point of view, it appears that std::vectors are a kind of "simple idea", so any complexity in the form of objects of different possible classes in their place ideologically makes no use.
In practical terms: If you do not have any data members in your derived class, you do not have any problems, not even in polymorphic usage. You only need a virtual destructor if the sizes of the base class and the derived class are different and/or you have virtual functions (which means a v-table).
BUT in theory: From [expr.delete] in the C++0x FCD: In the first alternative (delete object), if the static type of the object to be deleted is different from its dynamic type, the static type shall be a base class of the dynamic type of the object to be deleted and the static type shall have a virtual destructor or the behavior is undefined.
But you can derive privately from std::vector without problems.
I have used the following pattern:
class PointVector : private std::vector<PointType>
{
typedef std::vector<PointType> Vector;
...
using Vector::at;
using Vector::clear;
using Vector::iterator;
using Vector::const_iterator;
using Vector::begin;
using Vector::end;
using Vector::cbegin;
using Vector::cend;
using Vector::crbegin;
using Vector::crend;
using Vector::empty;
using Vector::size;
using Vector::reserve;
using Vector::operator[];
using Vector::assign;
using Vector::insert;
using Vector::erase;
using Vector::front;
using Vector::back;
using Vector::push_back;
using Vector::pop_back;
using Vector::resize;
...
If you follow good C++ style, the absence of virtual function is not the problem, but slicing (see https://stackoverflow.com/a/14461532/877329)
Why is absence of virtual functions not the problem? Because a function should not try to delete any pointer it receives, since it does not have an ownership of it. Therefore, if following strict ownership policies, virtual destructors should not be needed. For example, this is always wrong (with or without virtual destructor):
void foo(SomeType* obj)
{
if(obj!=nullptr) //The function prototype only makes sense if parameter is optional
{
obj->doStuff();
}
delete obj;
}
class SpecialSomeType:public SomeType
{
// whatever
};
int main()
{
SpecialSomeType obj;
doStuff(&obj); //Will crash here. But caller does not know that
// ...
}
In contrast, this will always work (with or without virtual destructor):
void foo(SomeType* obj)
{
if(obj!=nullptr) //The function prototype only makes sense if parameter is optional
{
obj->doStuff();
}
}
class SpecialSomeType:public SomeType
{
// whatever
};
int main()
{
SpecialSomeType obj;
doStuff(&obj);
// The correct destructor *will* be called here.
}
If the object is created by a factory, the factory should also return a pointer to a working deleter, which should be used instead of delete, since the factory may use its own heap. The caller can get it form of a share_ptr or unique_ptr. In short, do not delete anything you didn't get directly from new.
Yes it's safe as long as you are careful not to do the things that are not safe... I don't think I've ever seen anyone use a vector with new so in practice you'll likely be fine. However, it's not the common idiom in c++....
Are you able to give more information on what the algorithms are?
Sometimes you end up going down one road with a design and then can't see the other paths you might have taken - the fact that you claim to need to vector with 10 new algorithms rings alarm bells for me - are there really 10 general purpose algorithms that a vector can implement, or are you trying to make an object that is both a general purpose vector AND which contains application specific functions?
I'm certainly not saying that you shouldn't do this, it's just that with the information you've given alarm bells are ringing which makes me think that maybe something is wrong with your abstractions and there is a better way to achieve what you want.
I also inherited from std::vector recently, and found it to be very useful and so far I haven't experienced any problems with it.
My class is a sparse matrix class, meaning that I need to store my matrix elements somewhere, namely in an std::vector. My reason for inheriting was that I was a bit too lazy to write interfaces to all the methods and also I am interfacing the class to Python via SWIG, where there is already good interface code for std::vector. I found it much easier to extend this interface code to my class rather than writing a new one from scratch.
The only problem I can see with the approach is not so much with the non-virtual destructor, but rather some other methods, which I would like to overload, such as push_back(), resize(), insert() etc. Private inheritance could indeed be a good option.
Thanks!
This question is guaranteed to produce breathless pearl-clutching, but in fact there is no defensible reason for avoiding, or "unnecessarily multiplying entities" to avoid, derivation from a Standard container. The simplest, shortest possible expression is clearest, and best.
You do need to exercise all the usual care around any derived type, but there is nothing special about the case of a base from the Standard. Overriding a base member function could be tricky, but that would be unwise to do with any non-virtual base, so there is not much special here. If you were to add a data member, you would need to worry about slicing if the member had to be kept consistent with contents of the base, but again that is the same for any base.
The place where I have found deriving from a standard container particularly useful is to add a single constructor that does precisely the initialization needed, with no chance of confusion or hijacking by other constructors. (I'm looking at you, initialization_list constructors!) Then, you can freely use the resulting object, sliced -- pass it by reference to something expecting the base, move from it to an instance of the base, what have you. There are no edge cases to worry about, unless it would bother you to bind a template argument to the derived class.
A place where this technique will be immediately useful in C++20 is reservation. Where we might have written
std::vector<T> names; names.reserve(1000);
we can say
template<typename C>
struct reserve_in : C {
reserve_in(std::size_t n) { this->reserve(n); }
};
and then have, even as class members,
. . .
reserve_in<std::vector<T>> taken_names{1000}; // 1
std::vector<T> given_names{reserve_in<std::vector<T>>{1000}}; // 2
. . .
(according to preference) and not need to write a constructor just to call reserve() on them.
(The reason that reserve_in, technically, needs to wait for C++20 is that prior Standards don't require the capacity of an empty vector to be preserved across moves. That is acknowledged as an oversight, and can reasonably be expected to be fixed as a defect in time for '20. We can also expect the fix to be, effectively, backdated to previous Standards, because all existing implementations actually do preserve capacity across moves; the Standards just haven't required it. The eager can safely jump the gun -- reserving is almost always just an optimization anyway.)
Some would argue that the case of reserve_in is better served by a free function template:
template<typename C>
auto reserve_in(std::size_t n) { C c; c.reserve(n); return c; }
Such an alternative is certainly viable -- and could even, at times, be infinitesimally faster, because of *RVO. But the choice of derivation or free function should be made on its own merits, and not from baseless (heh!) superstition about deriving from Standard components. In the example use above, only the second form would work with the free function; although outside of class context it could be written a little more concisely:
auto given_names{reserve_in<std::vector<T>>(1000)}; // 2
Here, let me introduce 2 more ways to do want you want. One is another way to wrap std::vector, another is the way to inherit without giving users a chance to break anything:
Let me add another way of wrapping std::vector without writing a lot of function wrappers.
#include <utility> // For std:: forward
struct Derived: protected std::vector<T> {
// Anything...
using underlying_t = std::vector<T>;
auto* get_underlying() noexcept
{
return static_cast<underlying_t*>(this);
}
auto* get_underlying() const noexcept
{
return static_cast<underlying_t*>(this);
}
template <class Ret, class ...Args>
auto apply_to_underlying_class(Ret (*underlying_t::member_f)(Args...), Args &&...args)
{
return (get_underlying()->*member_f)(std::forward<Args>(args)...);
}
};
Inheriting from std::span instead of std::vector and avoid the dtor problem.
I have a boost::ptr_map which stores abstract base class (e.g. VectorWrapperBase) as values and this allows me to map strings to vectors of different types.
boost::ptr_map<std::string, VectorWrapperBase> memory_map;
//...
memory_map.insert(str_key, new VectorWrapper<T>());
This appears to work. However, when I have memory_map as a member of another class and attempt to store that class in an std::map, compilation fails.
class AgentMemory {
//...
private:
boost::ptr_map<std::string, VectorWrapperBase> memory_map;
};
std::map<std::string, AgentMemory> agent_map;
//...
agent_map.insert(std::pair<std::string, AgentMemory>(agent_name, AgentMemory()));
The last line fails with:
/SOMEPATH/boost_1_48_0/boost/ptr_container/clone_allocator.hpp:34
error: cannot allocate an object of abstract type ‘xyz::VectorWrapperBase’
Being new to C++, this is baffling.
I suspect that error is down to the map insertion copying the AgentMemory object which involves cloning the ptr_map. And since my VectorWrapper objects are not cloneable, the error is raised.
My questions are:
Why am I getting the error? (Are my suspicions even close to what's actually happening?)
How do I address this?
To address the compilation error, I've considered the following, but without much experience with C++ can't decide which is more appropriate:
Remove the pure specifier (= 0) so VectorWrapperBase is no longer abstract
This feels like a hack since VectorWrapperBase should never be instantiated
Make the VectorWrappers cloneable
This seems to work, but in my use case only empty containers are assigned to the top-level map so VectorWrappers within the inner ptr_map need never be cloned. The cloneability would therefore be there just to appease the compiler and does not reflect the actual usage.
Forget ptr_map and use a std::map and shared_ptr instead.
I'm less keen on this solution as I would like the lifetime of the vector wrappers to be linked to that of the map. I'm also a little concerned (perhaps unnecessarily so?) about the potential overheads of extensive use of shared_ptr in a heavily multi-threaded application.
The statement
agent_map.insert(std::pair<std::string, AgentMemory>(agent_name, AgentMemory()));
will call the default constructor of AgentMemory, which in turn will call the default constructor of boost::ptr_map<std::string, VectorWrapperBase>, which will try to call the non-existent constructor for the abstract base class VectorWrapperBase.
So you have to make sure that every constructor of types wrapping or inheriting VectorWrapperBase should always construct a concrete derived class. In your case, option 3 (a map of shared pointers to derived classes) could be sensible, but that depends on the larger context of your code.
Ok, this is really difficult to confess, but I do have a strong temptation at the moment to inherit from std::vector.
I need about 10 customized algorithms for vector and I want them to be directly members of the vector. But naturally I want also to have the rest of std::vector's interface. Well, my first idea, as a law-abiding citizen, was to have an std::vector member in MyVector class. But then I would have to manually reprovide all of the std::vector's interface. Too much to type. Next, I thought about private inheritance, so that instead of reproviding methods I would write a bunch of using std::vector::member's in the public section. This is tedious too actually.
And here I am, I really do think that I can simply inherit publicly from std::vector, but provide a warning in the documentation that this class should not be used polymorphically. I think most developers are competent enough to understand that this shouldn't be used polymorphically anyway.
Is my decision absolutely unjustifiable? If so, why? Can you provide an alternative which would have the additional members actually members but would not involve retyping all of vector's interface? I doubt it, but if you can, I'll just be happy.
Also, apart from the fact that some idiot can write something like
std::vector<int>* p = new MyVector
is there any other realistic peril in using MyVector? By saying realistic I discard things like imagine a function which takes a pointer to vector ...
Well, I've stated my case. I have sinned. Now it's up to you to forgive me or not :)
Actually, there is nothing wrong with public inheritance of std::vector. If you need this, just do that.
I would suggest doing that only if it is really necessary. Only if you can't do what you want with free functions (e.g. should keep some state).
The problem is that MyVector is a new entity. It means a new C++ developer should know what the hell it is before using it. What's the difference between std::vector and MyVector? Which one is better to use here and there? What if I need to move std::vector to MyVector? May I just use swap() or not?
Do not produce new entities just to make something to look better. These entities (especially, such common) aren't going to live in vacuum. They will live in mixed environment with constantly increased entropy.
The whole STL was designed in such way that algorithms and containers are separate.
This led to a concept of different types of iterators: const iterators, random access iterators, etc.
Therefore I recommend you to accept this convention and design your algorithms in such way that they won't care about what is the container they're working on - and they would only require a specific type of iterator which they'd need to perform their operations.
Also, let me redirect you to some good remarks by Jeff Attwood.
The main reason for not inheriting from std::vector publicly is an absence of a virtual destructor that effectively prevents you from polymorphic use of descendants. In particular, you are not allowed to delete a std::vector<T>* that actually points at a derived object (even if the derived class adds no members), yet the compiler generally can't warn you about it.
Private inheritance is allowed under these conditions. I therefore recommend using private inheritance and forwarding required methods from the parent as shown below.
class AdVector: private std::vector<double>
{
typedef double T;
typedef std::vector<double> vector;
public:
using vector::push_back;
using vector::operator[];
using vector::begin;
using vector::end;
AdVector operator*(const AdVector & ) const;
AdVector operator+(const AdVector & ) const;
AdVector();
virtual ~AdVector();
};
You should first consider refactoring your algorithms to abstract the type of container they are operating on and leave them as free templated functions, as pointed out by majority of answerers. This is usually done by making an algorithm accept a pair of iterators instead of container as arguments.
If you're considering this, you've clearly already slain the language pedants in your office. With them out of the way, why not just do
struct MyVector
{
std::vector<Thingy> v; // public!
void func1( ... ) ; // and so on
}
That will sidestep all the possible blunders that might come out of accidentally upcasting your MyVector class, and you can still access all the vector ops just by adding a little .v .
What are you hoping to accomplish? Just providing some functionality?
The C++ idiomatic way to do this is to just write some free functions that implement the functionality. Chances are you don't really require a std::vector, specifically for the functionality you're implementing, which means you're actually losing out on reusability by trying to inherit from std::vector.
I would strongly advise you to look at the standard library and headers, and meditate on how they work.
I think very few rules should be followed blindly 100% of the time. It sounds like you've given it quite a lot of thought, and are convinced that this is the way to go. So -- unless someone comes up with good specific reasons not to do this -- I think you should go ahead with your plan.
There is no reason to inherit from std::vector unless one wants to make a class that works differently than std::vector, because it handles in its own way the hidden details of std::vector's definition, or unless one has ideological reasons to use the objects of such class in place of std::vector's ones. However, the creators of the standard on C++ did not provide std::vector with any interface (in the form of protected members) that such inherited class could take advantage of in order to improve the vector in a specific way. Indeed, they had no way to think of any specific aspect that might need extension or fine-tune additional implementation, so they did not need to think of providing any such interface for any purpose.
The reasons for the second option can be only ideological, because std::vectors are not polymorphic, and otherwise there is no difference whether you expose std::vector's public interface via public inheritance or via public membership. (Suppose you need to keep some state in your object so you cannot get away with free functions). On a less sound note and from the ideological point of view, it appears that std::vectors are a kind of "simple idea", so any complexity in the form of objects of different possible classes in their place ideologically makes no use.
In practical terms: If you do not have any data members in your derived class, you do not have any problems, not even in polymorphic usage. You only need a virtual destructor if the sizes of the base class and the derived class are different and/or you have virtual functions (which means a v-table).
BUT in theory: From [expr.delete] in the C++0x FCD: In the first alternative (delete object), if the static type of the object to be deleted is different from its dynamic type, the static type shall be a base class of the dynamic type of the object to be deleted and the static type shall have a virtual destructor or the behavior is undefined.
But you can derive privately from std::vector without problems.
I have used the following pattern:
class PointVector : private std::vector<PointType>
{
typedef std::vector<PointType> Vector;
...
using Vector::at;
using Vector::clear;
using Vector::iterator;
using Vector::const_iterator;
using Vector::begin;
using Vector::end;
using Vector::cbegin;
using Vector::cend;
using Vector::crbegin;
using Vector::crend;
using Vector::empty;
using Vector::size;
using Vector::reserve;
using Vector::operator[];
using Vector::assign;
using Vector::insert;
using Vector::erase;
using Vector::front;
using Vector::back;
using Vector::push_back;
using Vector::pop_back;
using Vector::resize;
...
If you follow good C++ style, the absence of virtual function is not the problem, but slicing (see https://stackoverflow.com/a/14461532/877329)
Why is absence of virtual functions not the problem? Because a function should not try to delete any pointer it receives, since it does not have an ownership of it. Therefore, if following strict ownership policies, virtual destructors should not be needed. For example, this is always wrong (with or without virtual destructor):
void foo(SomeType* obj)
{
if(obj!=nullptr) //The function prototype only makes sense if parameter is optional
{
obj->doStuff();
}
delete obj;
}
class SpecialSomeType:public SomeType
{
// whatever
};
int main()
{
SpecialSomeType obj;
doStuff(&obj); //Will crash here. But caller does not know that
// ...
}
In contrast, this will always work (with or without virtual destructor):
void foo(SomeType* obj)
{
if(obj!=nullptr) //The function prototype only makes sense if parameter is optional
{
obj->doStuff();
}
}
class SpecialSomeType:public SomeType
{
// whatever
};
int main()
{
SpecialSomeType obj;
doStuff(&obj);
// The correct destructor *will* be called here.
}
If the object is created by a factory, the factory should also return a pointer to a working deleter, which should be used instead of delete, since the factory may use its own heap. The caller can get it form of a share_ptr or unique_ptr. In short, do not delete anything you didn't get directly from new.
Yes it's safe as long as you are careful not to do the things that are not safe... I don't think I've ever seen anyone use a vector with new so in practice you'll likely be fine. However, it's not the common idiom in c++....
Are you able to give more information on what the algorithms are?
Sometimes you end up going down one road with a design and then can't see the other paths you might have taken - the fact that you claim to need to vector with 10 new algorithms rings alarm bells for me - are there really 10 general purpose algorithms that a vector can implement, or are you trying to make an object that is both a general purpose vector AND which contains application specific functions?
I'm certainly not saying that you shouldn't do this, it's just that with the information you've given alarm bells are ringing which makes me think that maybe something is wrong with your abstractions and there is a better way to achieve what you want.
I also inherited from std::vector recently, and found it to be very useful and so far I haven't experienced any problems with it.
My class is a sparse matrix class, meaning that I need to store my matrix elements somewhere, namely in an std::vector. My reason for inheriting was that I was a bit too lazy to write interfaces to all the methods and also I am interfacing the class to Python via SWIG, where there is already good interface code for std::vector. I found it much easier to extend this interface code to my class rather than writing a new one from scratch.
The only problem I can see with the approach is not so much with the non-virtual destructor, but rather some other methods, which I would like to overload, such as push_back(), resize(), insert() etc. Private inheritance could indeed be a good option.
Thanks!
This question is guaranteed to produce breathless pearl-clutching, but in fact there is no defensible reason for avoiding, or "unnecessarily multiplying entities" to avoid, derivation from a Standard container. The simplest, shortest possible expression is clearest, and best.
You do need to exercise all the usual care around any derived type, but there is nothing special about the case of a base from the Standard. Overriding a base member function could be tricky, but that would be unwise to do with any non-virtual base, so there is not much special here. If you were to add a data member, you would need to worry about slicing if the member had to be kept consistent with contents of the base, but again that is the same for any base.
The place where I have found deriving from a standard container particularly useful is to add a single constructor that does precisely the initialization needed, with no chance of confusion or hijacking by other constructors. (I'm looking at you, initialization_list constructors!) Then, you can freely use the resulting object, sliced -- pass it by reference to something expecting the base, move from it to an instance of the base, what have you. There are no edge cases to worry about, unless it would bother you to bind a template argument to the derived class.
A place where this technique will be immediately useful in C++20 is reservation. Where we might have written
std::vector<T> names; names.reserve(1000);
we can say
template<typename C>
struct reserve_in : C {
reserve_in(std::size_t n) { this->reserve(n); }
};
and then have, even as class members,
. . .
reserve_in<std::vector<T>> taken_names{1000}; // 1
std::vector<T> given_names{reserve_in<std::vector<T>>{1000}}; // 2
. . .
(according to preference) and not need to write a constructor just to call reserve() on them.
(The reason that reserve_in, technically, needs to wait for C++20 is that prior Standards don't require the capacity of an empty vector to be preserved across moves. That is acknowledged as an oversight, and can reasonably be expected to be fixed as a defect in time for '20. We can also expect the fix to be, effectively, backdated to previous Standards, because all existing implementations actually do preserve capacity across moves; the Standards just haven't required it. The eager can safely jump the gun -- reserving is almost always just an optimization anyway.)
Some would argue that the case of reserve_in is better served by a free function template:
template<typename C>
auto reserve_in(std::size_t n) { C c; c.reserve(n); return c; }
Such an alternative is certainly viable -- and could even, at times, be infinitesimally faster, because of *RVO. But the choice of derivation or free function should be made on its own merits, and not from baseless (heh!) superstition about deriving from Standard components. In the example use above, only the second form would work with the free function; although outside of class context it could be written a little more concisely:
auto given_names{reserve_in<std::vector<T>>(1000)}; // 2
Here, let me introduce 2 more ways to do want you want. One is another way to wrap std::vector, another is the way to inherit without giving users a chance to break anything:
Let me add another way of wrapping std::vector without writing a lot of function wrappers.
#include <utility> // For std:: forward
struct Derived: protected std::vector<T> {
// Anything...
using underlying_t = std::vector<T>;
auto* get_underlying() noexcept
{
return static_cast<underlying_t*>(this);
}
auto* get_underlying() const noexcept
{
return static_cast<underlying_t*>(this);
}
template <class Ret, class ...Args>
auto apply_to_underlying_class(Ret (*underlying_t::member_f)(Args...), Args &&...args)
{
return (get_underlying()->*member_f)(std::forward<Args>(args)...);
}
};
Inheriting from std::span instead of std::vector and avoid the dtor problem.
i'm just reading meyers "More Effective C++ 35 New Ways" - item 33, and he suggest there
always to inherit from an abstract base class, and not a concrete.
one of the reason he claims, which i can't quite get , is that with inheriting from an abstract class, treating array polymorphically (item 3 in the book) is not a problem.
can someone suggest how is that ?
In addition i would like to hear if it's really always a good thing never to let the client instantiate a class which other derives from ? (meyers in his book is showing a problem with the assignment operator for example )
code example as requested:
CLASS BST {.... };
CLASS BlanacedBST:: public BST {....}
void printBSTArray(ostream& s, const BST array[],int Numelements)
{
for(int i=0;i < Numelements;i++)
{
s << array[i];
}
}
BST BSTArray[10];
printBSTArray(BSTArray); // works fine
BlanacedBST bBSTArray[10];
printBSTArray(bBSTArray); // undefined behaviour (beacuse the subscript operator advances the pointer according to BST chunk size)
then, he addes that avoiding concreate class (BlanacedBST) inheriting from another concreat class(BST) usually avoids this problem - this i don't get how.
While I think that avoiding inheritance from non-abstract classes is a good design guideline and something that should make you think twice about your design, I definitely do not think that it's in the category of 'never do this'.
I will say that classes designed to be inherited from that have data in them should probably be hiding their assignment operator because of the slicing issue.
I think there's a way to categorize classes that isn't often thought of, and I think that causes a lot of confusion. I think there are classes that are designed to be used by value, and classes that are designed to always be used by reference (meaning via a reference or a pointer or something like that).
In most object oriented languages user defined classes can only be used by reference, and there are a special class of 'primitive' types that can be used by value. One of C++'s big strengths is that you can create user defined classes that can be used by value. This can lead to some huge efficiency wins. In Java, for example, all of your points (to pick a random simple class) are heap allocated and need to be garbage collected, even though they're basically just two or three doubles stuck together with some nice 'final' support functions.
So classes that are designed to be used by reference should disable assignment and should seriously consider disabling copy construction and require people to use a 'make a copy of this' virtual function for that purpose. Notice that Java classes generally don't have anything like an assignment operator or standard copy constructor.
Classes that are designed to be used by value should generally not have virtual functions, though it may be very useful to have them be a part of an inheritance hierarchy. They can still be rather complex though because they can contain references to objects of classes designed to be used by reference.
If you need to treat a by reference class as being used by value you should use the handle/body design pattern or a smart pointer. The STL containers are all designed to be used on by value objects, so this is a fairly common problem.
Meyers does not say that you can create the array with no problems; he says that it will be more difficult for you to try to create it. The compiler will complain as soon as you try to initialise it, because you cannot create objects of the base class if it is abstract.