I have a class that adapts std::vector to model a container of domain-specific objects. I want to expose most of the std::vector API to the user, so that they may use familiar methods (size, clear, at, etc...) and standard algorithms on the container. This seems to be a reoccurring pattern for me in my designs:
class MyContainer : public std::vector<MyObject>
{
public:
// Redeclare all container traits: value_type, iterator, etc...
// Domain-specific constructors
// (more useful to the user than std::vector ones...)
// Add a few domain-specific helper methods...
// Perhaps modify or hide a few methods (domain-related)
};
I'm aware of the practice of preferring composition to inheritance when reusing a class for implementation -- but there's gotta be a limit! If I were to delegate everything to std::vector, there would be (by my count) 32 forwarding functions!
So my questions are... Is it really so bad to inherit implementation in such cases? What are the risks? Is there a safer way I can implement this without so much typing? Am I a heretic for using implementation inheritance? :)
Edit:
What about making it clear that the user should not use MyContainer via a std::vector<> pointer:
// non_api_header_file.h
namespace detail
{
typedef std::vector<MyObject> MyObjectBase;
}
// api_header_file.h
class MyContainer : public detail::MyObjectBase
{
// ...
};
The boost libraries seem to do this stuff all the time.
Edit 2:
One of the suggestions was to use free functions. I'll show it here as pseudo-code:
typedef std::vector<MyObject> MyCollection;
void specialCollectionInitializer(MyCollection& c, arguments...);
result specialCollectionFunction(const MyCollection& c);
etc...
A more OO way of doing it:
typedef std::vector<MyObject> MyCollection;
class MyCollectionWrapper
{
public:
// Constructor
MyCollectionWrapper(arguments...) {construct coll_}
// Access collection directly
MyCollection& collection() {return coll_;}
const MyCollection& collection() const {return coll_;}
// Special domain-related methods
result mySpecialMethod(arguments...);
private:
MyCollection coll_;
// Other domain-specific member variables used
// in conjunction with the collection.
}
The risk is deallocating through a pointer to the base class (delete, delete[], and potentially other deallocation methods). Since these classes (deque, map, string, etc.) don't have virtual dtors, it's impossible to clean them up properly with only a pointer to those classes:
struct BadExample : vector<int> {};
int main() {
vector<int>* p = new BadExample();
delete p; // this is Undefined Behavior
return 0;
}
That said, if you're willing to make sure you never accidentally do this, there's little major drawback to inheriting them—but in some cases that's a big if. Other drawbacks include clashing with implementation specifics and extensions (some of which may not use reserved identifiers) and dealing with bloated interfaces (string in particular). However, inheritance is intended in some cases, as container adapters like stack have a protected member c (the underlying container they adapt), and it's almost only accessible from a derived class instance.
Instead of either inheritance or composition, consider writing free functions which take either an iterator pair or a container reference, and operate on that. Practically all of <algorithm> is an example of this; and make_heap, pop_heap, and push_heap, in particular, are an example of using free functions instead of a domain-specific container.
So, use the container classes for your data types, and still call the free functions for your domain-specific logic. But you can still achieve some modularity using a typedef, which allows you to both simplify declaring them and provides a single point if part of them needs to change:
typedef std::deque<int, MyAllocator> Example;
// ...
Example c (42);
example_algorithm(c);
example_algorithm2(c.begin() + 5, c.end() - 5);
Example::iterator i; // nested types are especially easier
Notice the value_type and allocator can change without affecting later code using the typedef, and even the container can change from a deque to a vector.
You can combine private inheritance and the 'using' keyword to work around most of the problems mentioned above: Private inheritance is 'is-implemented-in-terms-of' and as it is private you cannot hold a pointer to the base class
#include <string>
#include <iostream>
class MyString : private std::string
{
public:
MyString(std::string s) : std::string(s) {}
using std::string::size;
std::string fooMe(){ return std::string("Foo: ") + *this; }
};
int main()
{
MyString s("Hi");
std::cout << "MyString.size(): " << s.size() << std::endl;
std::cout << "MyString.fooMe(): " << s.fooMe() << std::endl;
}
As everyone has already stated, STL containers do not have virtual destructors so inheriting from them is unsafe at best. I've always considered generic programming with templates as a different style of OO - one without inheritance. The algorithms define the interface that they require. It is as close to Duck Typing as you can get in a static language.
Anyway, I do have something to add to the discussion. The way that I have created my own template specializations previously is to define classes like the following to use as base classes.
template <typename Container>
class readonly_container_facade {
public:
typedef typename Container::size_type size_type;
typedef typename Container::const_iterator const_iterator;
virtual ~readonly_container_facade() {}
inline bool empty() const { return container.empty(); }
inline const_iterator begin() const { return container.begin(); }
inline const_iterator end() const { return container.end(); }
inline size_type size() const { return container.size(); }
protected: // hide to force inherited usage only
readonly_container_facade() {}
protected: // hide assignment by default
readonly_container_facade(readonly_container_facade const& other):
: container(other.container) {}
readonly_container_facade& operator=(readonly_container_facade& other) {
container = other.container;
return *this;
}
protected:
Container container;
};
template <typename Container>
class writable_container_facade: public readable_container_facade<Container> {
public:
typedef typename Container::iterator iterator;
writable_container_facade(writable_container_facade& other)
readonly_container_facade(other) {}
virtual ~writable_container_facade() {}
inline iterator begin() { return container.begin(); }
inline iterator end() { return container.end(); }
writable_container_facade& operator=(writable_container_facade& other) {
readable_container_facade<Container>::operator=(other);
return *this;
}
};
These classes expose the same interface as an STL container. I did like the effect of separating the modifying and non-modifying operations into distinct base classes. This has a really nice effect on const-correctness. The one downside is that you have to extend the interface if you want to use these with associative containers. I haven't run into the need though.
In this case, inheriting is a bad idea: the STL containers do not have virtual destructors so you might run into memory leaks (plus, it's an indication that STL containers are not meant to be inherited in the first place).
If you just need to add some functionality, you can declare it in global methods, or a lightweight class with a container member pointer/reference. This off course doesn't allow you to hide methods: if that is really what you are after, then there's no other option then redeclaring the entire implementation.
Virtual dtors aside, the decision to inherit versus contain should be a design decision based the class you are creating. You should never inherit container functionality just because its easier than containing a container and adding a few add and remove functions that seem like simplistic wrappers unless you can definitively say that the class you are creating is a kind-of the container. For instance, a classroom class will often contain student objects, but a classroom isn't a kind of list of students for most purposes, so you shouldn't be inheriting from list.
It is easier to do:
typedef std::vector<MyObject> MyContainer;
The forwarding methods will be inlined away, anyhow. You will not get better performance this way. In fact, you will likely get worse performance.
Always consider composition over inheritance.
Consider the case:
class __declspec(dllexport) Foo :
public std::multimap<std::string, std::string> {};
Then symbols of std::multimap will be exported into your dll, which may cause compilation error "std::multimap already defined".
Related
I have a class that adapts std::vector to model a container of domain-specific objects. I want to expose most of the std::vector API to the user, so that they may use familiar methods (size, clear, at, etc...) and standard algorithms on the container. This seems to be a reoccurring pattern for me in my designs:
class MyContainer : public std::vector<MyObject>
{
public:
// Redeclare all container traits: value_type, iterator, etc...
// Domain-specific constructors
// (more useful to the user than std::vector ones...)
// Add a few domain-specific helper methods...
// Perhaps modify or hide a few methods (domain-related)
};
I'm aware of the practice of preferring composition to inheritance when reusing a class for implementation -- but there's gotta be a limit! If I were to delegate everything to std::vector, there would be (by my count) 32 forwarding functions!
So my questions are... Is it really so bad to inherit implementation in such cases? What are the risks? Is there a safer way I can implement this without so much typing? Am I a heretic for using implementation inheritance? :)
Edit:
What about making it clear that the user should not use MyContainer via a std::vector<> pointer:
// non_api_header_file.h
namespace detail
{
typedef std::vector<MyObject> MyObjectBase;
}
// api_header_file.h
class MyContainer : public detail::MyObjectBase
{
// ...
};
The boost libraries seem to do this stuff all the time.
Edit 2:
One of the suggestions was to use free functions. I'll show it here as pseudo-code:
typedef std::vector<MyObject> MyCollection;
void specialCollectionInitializer(MyCollection& c, arguments...);
result specialCollectionFunction(const MyCollection& c);
etc...
A more OO way of doing it:
typedef std::vector<MyObject> MyCollection;
class MyCollectionWrapper
{
public:
// Constructor
MyCollectionWrapper(arguments...) {construct coll_}
// Access collection directly
MyCollection& collection() {return coll_;}
const MyCollection& collection() const {return coll_;}
// Special domain-related methods
result mySpecialMethod(arguments...);
private:
MyCollection coll_;
// Other domain-specific member variables used
// in conjunction with the collection.
}
The risk is deallocating through a pointer to the base class (delete, delete[], and potentially other deallocation methods). Since these classes (deque, map, string, etc.) don't have virtual dtors, it's impossible to clean them up properly with only a pointer to those classes:
struct BadExample : vector<int> {};
int main() {
vector<int>* p = new BadExample();
delete p; // this is Undefined Behavior
return 0;
}
That said, if you're willing to make sure you never accidentally do this, there's little major drawback to inheriting them—but in some cases that's a big if. Other drawbacks include clashing with implementation specifics and extensions (some of which may not use reserved identifiers) and dealing with bloated interfaces (string in particular). However, inheritance is intended in some cases, as container adapters like stack have a protected member c (the underlying container they adapt), and it's almost only accessible from a derived class instance.
Instead of either inheritance or composition, consider writing free functions which take either an iterator pair or a container reference, and operate on that. Practically all of <algorithm> is an example of this; and make_heap, pop_heap, and push_heap, in particular, are an example of using free functions instead of a domain-specific container.
So, use the container classes for your data types, and still call the free functions for your domain-specific logic. But you can still achieve some modularity using a typedef, which allows you to both simplify declaring them and provides a single point if part of them needs to change:
typedef std::deque<int, MyAllocator> Example;
// ...
Example c (42);
example_algorithm(c);
example_algorithm2(c.begin() + 5, c.end() - 5);
Example::iterator i; // nested types are especially easier
Notice the value_type and allocator can change without affecting later code using the typedef, and even the container can change from a deque to a vector.
You can combine private inheritance and the 'using' keyword to work around most of the problems mentioned above: Private inheritance is 'is-implemented-in-terms-of' and as it is private you cannot hold a pointer to the base class
#include <string>
#include <iostream>
class MyString : private std::string
{
public:
MyString(std::string s) : std::string(s) {}
using std::string::size;
std::string fooMe(){ return std::string("Foo: ") + *this; }
};
int main()
{
MyString s("Hi");
std::cout << "MyString.size(): " << s.size() << std::endl;
std::cout << "MyString.fooMe(): " << s.fooMe() << std::endl;
}
As everyone has already stated, STL containers do not have virtual destructors so inheriting from them is unsafe at best. I've always considered generic programming with templates as a different style of OO - one without inheritance. The algorithms define the interface that they require. It is as close to Duck Typing as you can get in a static language.
Anyway, I do have something to add to the discussion. The way that I have created my own template specializations previously is to define classes like the following to use as base classes.
template <typename Container>
class readonly_container_facade {
public:
typedef typename Container::size_type size_type;
typedef typename Container::const_iterator const_iterator;
virtual ~readonly_container_facade() {}
inline bool empty() const { return container.empty(); }
inline const_iterator begin() const { return container.begin(); }
inline const_iterator end() const { return container.end(); }
inline size_type size() const { return container.size(); }
protected: // hide to force inherited usage only
readonly_container_facade() {}
protected: // hide assignment by default
readonly_container_facade(readonly_container_facade const& other):
: container(other.container) {}
readonly_container_facade& operator=(readonly_container_facade& other) {
container = other.container;
return *this;
}
protected:
Container container;
};
template <typename Container>
class writable_container_facade: public readable_container_facade<Container> {
public:
typedef typename Container::iterator iterator;
writable_container_facade(writable_container_facade& other)
readonly_container_facade(other) {}
virtual ~writable_container_facade() {}
inline iterator begin() { return container.begin(); }
inline iterator end() { return container.end(); }
writable_container_facade& operator=(writable_container_facade& other) {
readable_container_facade<Container>::operator=(other);
return *this;
}
};
These classes expose the same interface as an STL container. I did like the effect of separating the modifying and non-modifying operations into distinct base classes. This has a really nice effect on const-correctness. The one downside is that you have to extend the interface if you want to use these with associative containers. I haven't run into the need though.
In this case, inheriting is a bad idea: the STL containers do not have virtual destructors so you might run into memory leaks (plus, it's an indication that STL containers are not meant to be inherited in the first place).
If you just need to add some functionality, you can declare it in global methods, or a lightweight class with a container member pointer/reference. This off course doesn't allow you to hide methods: if that is really what you are after, then there's no other option then redeclaring the entire implementation.
Virtual dtors aside, the decision to inherit versus contain should be a design decision based the class you are creating. You should never inherit container functionality just because its easier than containing a container and adding a few add and remove functions that seem like simplistic wrappers unless you can definitively say that the class you are creating is a kind-of the container. For instance, a classroom class will often contain student objects, but a classroom isn't a kind of list of students for most purposes, so you shouldn't be inheriting from list.
It is easier to do:
typedef std::vector<MyObject> MyContainer;
The forwarding methods will be inlined away, anyhow. You will not get better performance this way. In fact, you will likely get worse performance.
Always consider composition over inheritance.
Consider the case:
class __declspec(dllexport) Foo :
public std::multimap<std::string, std::string> {};
Then symbols of std::multimap will be exported into your dll, which may cause compilation error "std::multimap already defined".
I'm trying to achieve the following: Given an abstract class MemoryObject, that every class can inherit from, I have two subclasses: A Buffer and a BigBuffer:
template <typename T>
class MemoryObject
{
public:
typedef typename std::vector<T>::iterator iterator;
typedef typename std::vector<T>::const_iterator const_iterator;
[...] //Lot of stuff
virtual iterator begin() = 0;
virtual iterator end() = 0;
};
A Buffer:
template <typename T>
class Buffer: public MemoryObject<T>
{
public:
typedef typename std::vector<T>::iterator iterator;
iterator begin() { return buffer_.begin(); }
iterator end() { return buffer_.end(); };
[...] //Lot of stuff
private:
std::vector<T> buffer_;
};
And finally:
template <typename T>
class BigBuffer: public MemoryObject<T>
{
public:
[...] //Omitted, for now
private:
std::vector<Buffer<T>*> chunks_;
};
As you can see, a BigBuffer holds a std::vector of Buffer<T>*, so you can view a BigBuffer as an aggregation of Buffer(s). Futhermore, I have a bunch of functions that must work of every MemoryObject, so this is a real signature:
template <class KernelType, typename T>
void fill(CommandQueue<KernelType>& queue, MemoryObject<T>& obj, const T& value)
{
//Do something with obj
}
What's the point? - You may ask. The point is that I must implement iterators over these classes. I've already implemented them for Buffer, and is exactly what I need: be able to iterate over a Buffer, and access to ranges (for example b.begin(), b.begin() + 50).
Obviously I can't do the same for BigBuffer, because the real data (that is inside each Buffer' private variable buffer_) is scattered accross the memory. So I need a new class, let's call it BigBufferIterator, which can overload operator like * or +, allowing me to "jump" from a memory chunk to another without incurring in in segmentation fault.
The problems are two:
The iterator type of MemoryObject is different from the iterator
type of BigBuffer: the former is a std::vector<T>::iterator, the
latter a BigBufferIterator. My compiler obviously complains
I want be able to preserve the genericity of my functions signatures
passing to them only a MemoryObject<T>&, not specializing them for
each class type.
I've tried to solve the first problem adding a template parameter classed Iterator, and giving it a default argument to each class, with a model loosely based to Alexandrescu's policy-based model. This solution solved the first issue, but not the second: my compiled still complains, telling me: "Not known conversion from BigBuffer to MemoryObject", when I try to pass a BigBuffer to a function (for example, the fill() ). This is because Iterator types are different..
I'm really sorry for this poem, but It was the only way to proper present my problem to you. I don't know why someone would even bother in reading all this stuff, but I'll take pot luck.
Thanks in advance, only just for having read till this point.
Humbly,
Alfredo
They way to go is to use the most general definition as the iterator type of the base. That is, you want to treat the data in a Buffer as just one segment while the BigBuffer is a sequence of the corresponding segments. This is a bit unfortunate because it means that you treat your iterator for the single buffer in Buffer as if it may be multiple buffers, i.e. you have a segmented structure with just one segment. However, compared to the alternative (i.e. a hierarchy of iterators with virtual functions wrapped by a handle giving value semantics to this mess) you are actually not paying to bad a cost.
I encountered the same problem in a different context; let me restate it.
You have a Base class (which could be abstract), which is iterable via its BaseIterator.
You have a Child subclass, which differs in implementation slightly, and for which you have a different specialized ChildIterator.
You have custom functions that work with Base, and rely on its iterability.
It is not feasible to generate a template specialization of the custom functions for each subclass of Base. Possible reasons may be:
huge code duplication;
you distribute this code as a library and other developers are going to subclass Base;
other classes will take some reference or pointer to Base and apply those custom functions, or more generically:
Base implements some logic that is going to be uses in contexts where do not know any of the subclasses (and writing completely templated code is too cumbersome).
Edit: Another possibility would be using type erasure. You would hide the real iterator that you're using behind a class of a fixed type. You would have to pay the cost of the virtual functions call though. Here is an implementation of a any_iterator class which implements exactly iterator type erasure, and some more background on it.
The only effective solution I could find was to write a multi-purpose iterator that can iterate over all possible containers, possibly exploiting their internals (to avoid copy-pasting the iterator code for every subclass of Base):
// A bigger unit of memory
struct Buffer;
class Iterator {
// This allows you to know which set of methods you need to call
enum {
small_chunks,
big_chunks
} const _granularity;
// Merge the data into a union to save memory
union {
// Data you need to know to iterate over ints
struct {
std::vector<int> const *v;
std::vector<int>::const_iterator it;
} _small_chunks;
// Data you need to know to iterate over buffer chunks
struct {
std::vector<Buffer *> const *v;
std::vector<Buffer *>::const_iterator it;
} _big_chunks;
};
// Every method will have to choose what to do
void increment() {
switch (_granularity) {
case small_chunks:
_small_chunks.it++;
break;
case big_chunks:
_big_chunks.it++;
break;
}
}
public:
// Cctors are almost identical, but different overloads choose
// different granularity
Iterator(std::vector<int> const &container)
: _granularity(small_chunks) // SMALL
{
_small_chunks.v = &container;
_small_chunks.it = container.begin();
}
Iterator(std::vector<Buffer *> const &container)
: _granularity(big_chunks) // BIG
{
_big_chunks.v = &container;
_big_chunks.it = container.begin();
}
// ... Implement all your methods
};
You can use the same class for both Base and Child, but you need to initialize it differently. At this point you can make begin and end virtual and return an Iterator constructed differently in each subclass:
class Base {
public:
virtual Iterator begin() const = 0;
};
class IntChild : public Base {
std::vector<int> _small_mem;
public:
virtual Iterator begin() const {
// Created with granularity 'small_chunks'
return Iterator(_small_mem);
}
// ...
};
class BufferChild : public Base {
std::vector<Buffer *> _big_mem;
public:
virtual Iterator begin() const {
// Created with granularity 'big_chunks'
return Iterator(_big_mem);
}
// ...
};
You will pay a small price in performance (because of the switch statements) and in code duplication, but you will be able to use any generic algorithm from <algorithm>, to use range-loop, to use Base only in every function, and it's not stretching the inheritance mechanism.
// Anywhere, just by knowing Base:
void count_free_chunks(Base &mem) {
// Thanks to polymorphism, this code will always work
// with the right chunk size
for (auto const &item : mem) {
// ...this works
}
// This also works:
return std::count(mem.begin(), mem.end(), 0);
}
Note that the key point here is that begin() and end() must return the same type. The only exception would be if Child's methods would shadow Base's method (which is in general not a good practice).
One possible idea would be to declare an abstract iterator, but this is not so good. You would have to use all the time a reference to the iterator. Iterator though are supposed to be carried around as lightweight types, to be assignable and easily constructible, so it would make coding a minefield.
Actually, I have a design question here. Its very simple but the point is:
I have one C++ class that has a STL vector declared as a private member. But the clients of that class need to iterate over this vector.
In C# we have a very handy statement, the Yield, that in cases like that, you write a function returning an IEnumerable and it "yields" you a nice way to iterate over a private container inside that class.
I'm just trying to find an elegant solution for C++, instead of using methods like GetValue(int idx).
Any suggestions?
Example:
class Fat
{
public:
Fat();
// some code here ...
private:
void LoadSectors(SECT startPoint);
std::vector<SECT>sectors;
};
class Storage
{
public:
Storage(string CompoundFile);
//For example, this method will receive a ref to my fat system and iterate over
//the fat array in order to read every sector.
LoadStrem(Fat& fat);
};
This is far simple example.
There's no syntactic sugar in C++ analogous to yield in C#. If you want to create a class, instances of which should be iterable in the same way stock STL collections are, then you have to implement an iterator for your class, expose it as ::iterator on your type, and provide begin() and end() member functions.
You can either create an accessor function which returns a reference (or preferably const reference) to the vector, or you can create begin() and end() accessor functions which return the appropriate vector iterators.
It always hurts when you need to publish the innards of your class...
You may be able to solve it by providing algorithms as the stl does: provide a foreach function on the object's interface.
class S {
std::vector<int> v;
public:
//... and some methods to populate the vector
template< typename F > F& foreach( F& f ) {
return std::for_each( v.begin(), v.end(), f );
}
};
This way, the class remains 'closed', but you have the flexibility you need. You can also add a copy function, and maybe a transform; these are the ones I most frequently need.
Let your class expose iterators.
class Fat
{
public:
typedef std::vector<SECT>::iterator iterator;
iterator begin() { return sectors.begin(); }
iterator end() { return sectors.end(); }
Fat();
// some code here ...
private:
void LoadSectors(SECT startPoint);
std::vector<SECT>sectors;
Then the surrounding code can traverse the elements of the vector freely, through just a pair of iterators.
Are there any suggestions on how to use a nested class iterator
in an ABC in C++ ? Note that, I also want to have a virtual function returning an
instance of this class.
More specifically here's my approach:
class ABC {
typedef iterator<forward_iterator_tag, MyType> MyTypeIter;
virtual MyTypeIter *begin() = 0;
};
class Foo : ABC {
MyTypeIter : public ABC::MyTypeIter;
virtual MyTypeIter *begin();
};
ABC::MyTypeIter *Foo::begin()
{
Foo::MyTypeIter *ret;
ret = new Foo::MyTypeIter(...);
return ret;
}
Is there a better approach than this (e.g. one that does not use pointers) ?
What is your problem? A nested class behaves the same way as a top-level class, so you may return its objects just as you would have returned any other.
Take a look on how iterators are implemented for std::vector class, for example.
I prefer to keep iterators interal to the class and exposing only an interface for iteration.
For example, you can implement a List<> with the methods:
void prepare_iteration() // reset the internal iterator
bool step_iteration() // move internal iterator
DATA_TYPE & read() // return the data by using the iterator
write( DATA_TYPE & ) // update the data by using the iterator
In this example the iterator can be a simple node pointer and it's never exposed to the user.
I find this approach much easier and safer than iterator objects.(well the 'safer' part needs a lot of discussion)
The above interface can be implemented as an abstract class.
Your container (or whatever) classes can inherit it and implement it.
I know that's not the answer that you are looking for but it's just an alternative idea to design your classes.
Consider the following example code:
class Foo
{
};
class Bar : public Foo
{
};
class FooCollection
{
protected:
vector<shared_ptr<Foo> > d_foos;
};
class BarCollection : public FooCollection
{
public:
vector<shared_ptr<Bar> > &getBars()
{
// return d_foos won't do here...
}
};
I have a problem like this in my current project. The client code uses BarCollection, which stores pointers to Bars in d_foos which is declared in FooCollection. I'd now like to expose the collection of pointers to Bars to the client code. I could just give the client code access to the vector of pointers to Foos and cast these to pointers to Bars in the client code, but this feels wrong since the client doesn't have to know about Foo's existence.
I could also define a get() member that retrieves objects from d_foos and casts them, but this feels quite clumsy. Preferably, I'd like to just return d_foos as a vector<shared_ptr<Bar> > &, but I cannot seem to do this.
It could also be that my design is just plain wrong. It seemed to most natural solution though, as Bar is a specialization of Foo and BarCollection is a specialization of FooCollection and they share functionality.
Could you suggest nice solutions to implement getBars in BarCollection or better design alternatives?
Edit:
Turns out my design was bad indeed. BarCollection is not a FooCollection, despite of requiring all of FooCollection's functionality. My current solution based on the answers below -- which is a lot cleaner -- is now:
class Foo
{
};
class Bar : public Foo
{
};
template<class T>
class Collection
{
vector<shared_ptr<T> > d_items;
};
typedef Collection<Foo> FooCollection;
class BarCollection : public Collection<Bar>
{
// Additional stuff here.
};
Thanks for all the excellent suggestions and examples!
I would suggest exposing iterators from your container classes, instead of the member container. That way it won't matter what the container type is.
The problem is you are trying to mix-and-match two different, pretty-much independent kinds of polymorphism in a way that won't work. The compile-time, type-safe polymorphism of templates won't allow you to substitute a base type for a derived type. C++'s template system makes no association between
class<Foo>
and
class<Bar>
One suggestion might be to create a Foo derived adapter that would cast down to the correct class:
template <class derived, class base>
class DowncastContainerAdapter
{
private:
std::vector< boost::shared_ptr<base> >::iterator curr;
std::vector< boost::shared_ptr<base> >::const_iterator end;
public:
DowncastContainerAdapter(/*setup curr & end iterators*/)
{
// assert derived actually is derived from base
}
boost::shared_ptr<derived> GetNext()
{
// increment iterator
++curr;
return dynamic_cast<base>(*curr);
}
bool IsEnd()
{
return (curr == end);
}
};
Note this class will have the same problem as an iterator, operation on the vector may invalidate this class.
Another thought
You may not realize it, but it may be perfectly fine to just return a vector of Foo. The user of Bar already must have full knowledge of Foo, since by including Bar.h they must get Foo.h through Bar.h. The reason being that for Bar to inherit from Foo, it must have full knowledge of the class via Foo.h. I would suggest rather than using the solution above, if it's possible make Foo (or a super class of Foo) an interface class and pass around vectors of pointers to that interface class. This is a pretty commonly seen pattern and won't raise the eyebrows that this wonky solution I came up with might :). Then again you may have your reasons. Good luck either way.
template<class T>
class MyContainer {
vector<shared_ptr<T> > d_foos;
public:
vector<shared_ptr<T> > & getVector();
};
class FooCollection : public MyContainer<Foo> {
};
class BarCollection : public MyContainer<Bar> {
};
The question is, why would you do that? If you give the user a collection of pointers to Bar, you assume, that there are only Bars in it, so internally storing the pointers in a collection to Foo makes no sense. If you store different subtypes of Foo in your collection of pointer to Foo, you cannot return it as a collection of pointers to Bar, since not all objects in there are Bars.
In the first case, (you know that you have only bars) you should use a templated approach as suggested above.
Otherwise, you have to rethink, what you really want.
Can you not replace this with a Collection templatized on either Foo / Bar?, something like this
class Collection<T> {
protected:
vector<shared_ptr<T> > d_foos;
};
typedef Collection<Foo> FooCollection;
typedef Collection<Bar> BarCollection;
Do you have a special need to have BarCollection derived from FooCollection? Because generally a BarCollection is not a FooCollection, usually a lot of the things that can be done with a FooCollection should not be done with a BarCollection. For example:
BarCollection *bc = new BarCollection();
FooCollection *fc = bc; // They are derived from each other to be able to do this
fc->addFoo(Foo()); // Of course we can add a Foo to a FooCollection
Now we have added a Foo object to what is supposed to be a BarCollection. If the BarCollection tries to access this newly added element and expects it to be a Bar, all kinds of ugly things will happen.
So usually you want to avoid this and don't have your collection classes derived from each other. See also questions about casting containers of derived types for more answers on this topic...
First of all, let's talk about shared_ptr. Do you know about: boost::detail::dynamic_cast_tag ?
shared_ptr<Foo> fooPtr(new Bar());
shared_ptr<Bar> barPtr(fooPtr, boost::detail::dynamic_cast_tag());
This is a very handy way. Under the cover it just performs a dynamic_cast, nothing fancy there but an easier notation. The contract is the same that the classic one: if the object pointed to does not actually is a Bar (or derived from it), then you get a null pointer.
Back to your question: BAD CODE.
BarCollection is NOT a FooCollection, as mentioned you are therefore in trouble because you could introduce other elements in the vector of pointers that Bar ones.
I won't extend on that though, because this is beyond the question at hand, and I think that we (as those trying to answer) should restrain ourselves from that.
You cannot pass a reference, but you can pass a View.
Basically, a View is a new object that act as a Proxy to the old one. It's relatively easy using Boost.Iterators from example.
class VectorView
{
typedef std::vector< std::shared_ptr<Foo> > base_type;
public:
typedef Bar value_type;
// all the cluttering
class iterator: boost::iterator::iterator_adaptor<
iterator,
typename base_type::iterator,
std::shared_ptr<Bar>
>
{
typename iterator_adaptor::reference dereference() const
{
// If you have a heart weakness, you'd better stop here...
return reinterpret_cast< std::shared_ptr<Bar> >(this->base_reference());
}
};
// idem for const_iterator
// On to the method forwarding
iterator begin() { return iterator(m_reference.begin()); }
private:
base_type& m_reference;
}; // class VectorView
The real problem here is of course the reference bit. Getting a NEW shared_ptr object is easy, and allow to perform a dynamic_cast as required. Getting a reference to the ORIGINAL shared_ptr but interpreted as the required type... is really not what I like to see in code.
Note:
There might be a way to do better than that using Boost.Fusion transform_view class, but I could not figure it out.
In particular, using transform_view I can get shared_ptr<Bar> but I can't get a shared_ptr<Bar>& when I dereference my iterator, which is annoying given that the only use of returning a reference to the underlying vector (and not a const_reference) is to actually modify the structure of the vector and the objects it contains.
Note 2:
Please consider refactoring. There have been excellent suggestions there.