I have a method
void foo(list<shared_ptr<Base>>& myList);
Which I'm trying to call with a two different types of lists, one of DerivedClass1 and one of DerivedClass2
list<shared_ptr<DerivedClass1>> myList1;
foo(myList1);
list<shared_ptr<DerivedClass2>> myList2;
foo(myList2);
However this obviously generates a compiler error
error: a reference of type "std::list<boost::shared_ptr<Base>, std::allocator<boost::shared_ptr<Base>>> &" (not const-qualified) cannot be initialized with a value of type "std::list<boost::shared_ptr<DerivedClass1>, std::allocator<boost::shared_ptr<DerivedClass1>>>"
Is there any easy way to cast a container of shared_ptr? Of alternate containers that can accomplish this?
Update: Thanks to everyone who responded. Working within the confines of the language, it seems the best way to go while keeping the method 'as-is' is to use a container of shared_ptr and pass exactly that in (creating a new list at the call site).
I guess I pretty much already knew this, but I remembered reading about other parts of the boost library dealing with containers of shared_ptr and thought maybe it was solved more elegantly by someone else already. From my own further research however these seem to be geared more towards reducing overhead of shared_ptr in cases where a number of pointers are owned exclusively (therefore requiring a single lock per container rather than one per object in the container).
Thanks again, you guys are all awesome!
Change foo so that it follows STL conventions and takes iterators:
template< typename It >
void foo(It begin, It end);
Then pass iterators into your lists
list<shared_ptr<DerivedClass1>> myList1;
foo(myList1.begin(), myList1.end());
list<shared_ptr<DerivedClass2>> myList2;
foo(myList2.begin(), myList2.end());
And everything should work just fine.
Edit:
Note that this isn't anything special to smart pointers. A std::list<T1*>& cannot be initialized with a std::list<T2*>, even if T2 derives from T1.
You can't cast a container of one type to a container of another type. There are a few ways to create a new container from an existing container, if the type of object stored by the existing container is convertible to the type of object stored by the new container:
You can use std::copy to do the conversion element-by-element:
list<shared_ptr<Base> > baseList;
list<shared_ptr<Derived> > derivedList;
std::copy(derivedList.begin(), derivedList.end(), std::back_inserter(baseList));
You can also directly construct baseList using the begin and end iterators from derivedList:
list<shared_ptr<Base> > baseList(derivedList.begin(), derivedList.end());
If your function can take a const reference instead, you can construct a temporary in the function call:
typedef list<shared_ptr<Base> > BaseList;
foo(BaseList(derivedList.begin(), derivedList.end()));
Note that list<shared_ptr<Base>> and list<shared_ptr<DerivedClass1>> (or even shared_ptr<Base> and shared_ptr<DerivedClass1>) are completely different types, even if DerivedClass1 inherits from Base. So casting one to the other would in fact be wrong.
Consider the case when foo attempts to add new elements of type Base to myList. If you could cast myList1 into a list<shared_ptr<Base>> (e.g. with an old-style C cast), and passed it to foo, the result would not be nice.
So the only proper solution I see is to create a new list<shared_ptr<Base>> object and copy the contents of the other list into it, then pass that to foo, as is demonstrated by #James McNellis.
The classical explanation why that wouldn't be a good idea goes as follows:
If you have a list<Apple*> nicely filled with Apples, and you were able to pass it to a function accepting a list<Fruit*>, then there is no stopping the function from putting new Oranges in the list. A list<Fruit*> is simply not substitutable for a list<Apple*> and there exists no relationship.
If you want a function that could work both with a list<Apple*> and list<Orange*>, you could use templates (whether with Iterators, or taking the Container itself, or taking a list<T*> - entirely your choice.
If you really badly wanted to pass a list<Apple*> as if a list<Fruit*>, then I suppose it would be technically possible to come up with proxies for the container / iterators, that use type erasure and what-not to cast the pointers internally / stop invalid casts (putting Oranges into list of Apples), so that you could make the function instead take a list_proxy<Fruit*> which accepts lists of derived types.
Something along the lines of (just a beginning):
#include <list>
#include <boost/shared_ptr.hpp>
template <class T>
class ListWrapperBaseAux
{
public:
virtual ~ListWrapperBaseAux() {}
virtual T* front() = 0;
};
template <class T, class U>
class ListWrapperAux: public ListWrapperBaseAux<T>
{
std::list<U*>* list_ref;
public:
ListWrapperAux(std::list<U*>* list_ref): list_ref(list_ref) {}
virtual T* front() { return dynamic_cast<T*>(list_ref->front()); }
};
template <class T>
class ListProxy
{
boost::shared_ptr<ListWrapperBaseAux<T> > list_ref;
public:
template <class U>
ListProxy(std::list<U*>& li): list_ref(new ListWrapperAux<T, U>(&li)) {}
T* front() { return list_ref->front(); }
};
struct Fruit
{
virtual ~Fruit() {}
};
struct Apple: Fruit {};
struct Orange: Fruit {};
void bake(ListProxy<Fruit> li)
{
Fruit* f = li.front();
}
int main()
{
std::list<Apple*> apples;
bake(apples);
std::list<Orange*> oranges;
bake(oranges);
}
Related
Consider these classes.
class Base
{
...
};
class Derived : public Base
{
...
};
this function
void BaseFoo( std::vector<Base*>vec )
{
...
}
And finally my vector
std::vector<Derived*>derived;
I want to pass derived to function BaseFoo, but the compiler doesn't let me. How do I solve this, without copying the whole vector to a std::vector<Base*>?
vector<Base*> and vector<Derived*> are unrelated types, so you can't do this. This is explained in the C++ FAQ here.
You need to change your variable from a vector<Derived*> to a vector<Base*> and insert Derived objects into it.
Also, to avoid copying the vector unnecessarily, you should pass it by const-reference, not by value:
void BaseFoo( const std::vector<Base*>& vec )
{
...
}
Finally, to avoid memory leaks, and make your code exception-safe, consider using a container designed to handle heap-allocated objects, e.g:
#include <boost/ptr_container/ptr_vector.hpp>
boost::ptr_vector<Base> vec;
Alternatively, change the vector to hold a smart pointer instead of using raw pointers:
#include <memory>
std::vector< std::shared_ptr<Base*> > vec;
or
#include <boost/shared_ptr.hpp>
std::vector< boost::shared_ptr<Base*> > vec;
In each case, you would need to modify your BaseFoo function accordingly.
Instead of passing the container object (vector<>), pass in begin and end iterators like the rest of the STL algorithms. The function that receives them will be templated, and it won't matter if you pass in Derived* or Base*.
This problem occurs in programming languages that have mutable containers. You cannot pass around a mutable bag of apples as a bag of fruit because you cannot be sure that someone else does not put a lemon into that bag of fruit, after which it no longer qualifies as a bag of apples. If the bag of apples were not mutable, passing it around as a bag of fruit would be fine. Search for covariance/contravariance.
one option is to use a template
template<typename T>
void BaseFoo( const std::vector<T*>& vec)
{
...
}
The drawback is that the implementation has to be in the header and you will get a little code bloat. You will wind up with different functions being instantiated for each type, but the code stays the same. Depending on the use case it's a quick and dirty solution.
Edit, I should note the reason we need a template here is because we are trying to write the same code for unrelated types as noted by several other posters. Templates allow you do solve these exact problems. I also updated it to use a const reference. You should also pass "heavy" objects like a vector by const reference when you don't need a copy, which is basically always.
Generally you would start with a container of base pointers, not the other way.
If you dealing with a third-party library, and this is your only hope, then you can do this:
BaseFoo (*reinterpret_cast<std::vector<Base *> *>(&derived));
Otherwise fix your code with one of the other suggesstions.
Taking Matt Price's answer from above, given that you know in advance what types you want to use with your function, you can declare the function template in the header file, and then add explicit instantiations for those types:
// BaseFoo.h
template<typename T>
void BaseFoo( const std::vector<T*>& vec);
// BaseFoo.cpp
template<typename T>
void BaseFoo( const std::vector<T*>& vec);
{
...
}
// Explicit instantiation means no need for definition in the header file.
template void BaseFoo<Base> ( const std::vector<Base*>& vec );
template void BaseFoo<Derived> ( const std::vector<Derived*>& vec );
If std::vector supported what you're asking for, then it would be possible to defeat the C++ type system without using any casts (edit: ChrisN's link to the C++ FAQ Lite talks about the same issue):
class Base {};
class Derived1 : public Base {};
class Derived2 : public Base {};
void pushStuff(std::vector<Base*>& vec) {
vec.push_back(new Derived2);
vec.push_back(new Base);
}
...
std::vector<Derived1*> vec;
pushStuff(vec); // Not legal
// Now vec contains a Derived2 and a Base!
Since your BaseFoo() function takes the vector by value, it cannot modify the original vector that you passed in, so what I wrote would not be possible. But if it takes a non-const reference and you use reinterpret_cast<std::vector<Base*>&>() to pass your std::vector<Derived*>, you might not get the result that you want, and your program might crash.
Java arrays support covariant subtyping, and this requires Java to do a runtime type check every time you store a value in an array. This too is undesirable.
They are unrelated types -- you can't.
There is a templated class, let it be
template<typename T> class A { std::vector<T> data; };
The problem I am facing here is, users can create several types of this class, but I need to track them, best case is I have a reference of these objects in another vector, but that would not work since all types are different.
Can you recommend a good design pattern which can encapsulate this.
I can store pointers and then typecast it, but its not elegant.
I can change the architecture as well, if the solution provided is good enough.
The basic question I am trying to solve is, I have a class of vector of custom types, how do I store them.
As previous comments stated - you first need to make sure this is what you need.
With that been said, I had a similar requirement in a project of mine, which I eventually solved with inheritance and PIMPL, as follows:
class A{
private:
struct Abstract {
virtual void f() = 0;
};
template <typename T>
struct Implementation : public Abstract {
std::vector<T> data;
virtual void f() {...}
};
std::unique_ptr<Abstract> impl;
public:
template <typename T>
A(): impl(std::make_unique<Implementation<T> >()){}
void f() {impl->f();}
};
This allows you to create a container of objects of type A, and access them via the public interface defined therein (the method f). The underlying type T of each A object is specified on construction. All other implementation details specific to the type T are hidden.
The solution suffers the inherent overhead of virtual functions. I'm not sure how it compares to the std::any approach performance-wise.
std::any is the modern c++17 solution. Specifically, you should use
A<int> a;
a.data.push_back(0);
// fill refernces...
std::vector<std::any> refernces;
refernces.push_back(&a.data[0]);
// check which type is active.
if(int** iPtr = std::any_cast<int*>(&references[0]); iPtr != nullptr)
{
// its an int*
int& i = **iPtr;
// do something with i.
}
These pointers can point into the A<int>::data and A<double>::data vectors.
For a complete reference, see here https://en.cppreference.com/w/cpp/utility/any.
I am currently working on a project that requires me to have a vector storing pointers to objects of the same class but with different template values. I want to use shared_ptrs for reasons that I won't get too deep into (mainly that if I want to share a Column between two ColumnLists).
I need to be able to return a casted pointer from a function (as seen below).
So here is a very simplified version:
template <typename ColType>
class Column {
std::vector<ColType>;
};
template <typename ...TypesOfColumns>
class ColumnList {
private:
std::vector< std::shared_ptr<void> > columnsVector;
/* Needs to have a vector storing pointers to multiple Columns
all with different template values */
public:
template <typename ReturnType> std::shared_ptr<ReturnType> GetPointer(int index)
{
return std::static_pointer_cast<ReturnType>(columnsVector.at(index));
};
};
I am wondering if I am getting to some type of undefined behavior here? Will it work as I am hoping: will returning the casted type just add one to the Strong Reference Counter of the void pointer? Can one be deleted before the other one is? Am I risking a memory leak where one gets deleted and the other does not (I doubt this one to be the case)?
As always, thanks for all of the help!!!
static_pointer_cast follows the same rules that static_cast does. It is legal to downcast a pointer-to-base to a pointer-to-derived as long as derived derives from base. As long as you are sure that columnsVector.at(index) contains a shared_ptr<ReturnType> (that is, contains a shared_ptr<void> that originated as a shared_ptr<ReturnType>), what you are doing is legal. If you don't know for sure, you should use dynamic_pointer_cast.
Further, regarding memory, the output of static_pointer_cast shares the same underlying control block as the input, so it's safe that way too. Even if the input were to be deleted first, the output would remain a legal owner of the shared resource, and vice-versa.
IMO much better would be having an abstract column as the base class for the template:
struct AbstractColumn {
virtual ~AbstractColumn() {}
...
};
template<typename Type>
struct Column : AbstractColumn {
std::vector<Type> data;
};
That would provide a place where to put generic methods (e.g. conversion to string, parsing from string etc.) and will also allow you to use pointers to the base class instead of pointers to void.
I'm trying to use templates to get std:list of items, where each item has a pointer to the list which contains it, but I keep hitting a compiler message.
Here's a very stripped down version of the code.
template <class E> class Item
{
public:
E* owner; // pointer to list that owns us.
};
template <class E> class BaseList: public std::list<E>
{
protected:
typedef std::list<E> inherited;
public:
void push_back(const E &e)
{
E tmp(e);
tmp.owner = this; // This line gives the error.
inherited::push_back(tmp);
}
};
class MyList;
class MyItem : public Item<MyList>
{
};
class MyList : public BaseList<MyItem>
{
};
void foo() // test code to instantiate template
{
MyList l;
MyItem m;
l.push_back(m);
}
However, my compiler barfs at the line:-
tmp.owner = this;
Error is:
[BCC32 Error] Unit7.cpp(30): E2034 Cannot convert 'BaseList<MyItem> * const' to 'MyList *'
It's like "this" has somehow become const, but I can't see why. Compiler is Codegear C++Builder 2009.
I admit I'm not 100% happy using templates, so I'm unsure if this is my problem or the compilers. The same code without template use compiles fine, but obviously that's not what I want, as I have several item/list classes that want to work this way.
Also, is there a better technique that would avoid having all the "owner" pointers in each item?
EDIT: I think I stripped the example down too far: "MyList" actually introduces new methods, which "MyItem" must then access through the "owner" pointer.
SUMMARY: Thanks for all comments and answers. As the accepted answer says, the problem is simply one of type incompatibility between pointer to a BaseList vs. MyList.
The issues raised about deriving from STL containers and alternative designs are also helpful, but the solution I've used is essentially identical to Luc Touraille's one below.
At line 30, "this" is a pointer to a BaseList<MyIteM>, not a MyList. You can substitute a class with a derived one, but not the other way around.
You can either typedef MyList to be a BaseList<MyItem>, like so:
typedef BaseList<MyItem> MyList
or let MyItem derive from Item<BaseList<MyItem> > instead.
When you derive from a type, you create a different type. When you typedef, you create an alias for that type. So when you typedef the compiler will accept this.
In addition to the answers you already have, I would also point out that the standard library collection classes are not intended to be derived from, as they do not have virtual destructors, and none of their member functions is virtual.
Shouldn't it be tmp.owner = static_cast<MyList*>(this). The type of E is MyList in the MyItem hence E* will be MyList* . The type of this pointer will be BaseList*, hence compiler gives the error that you can not convert the base class pointer to the derived class pointer.
It's hard to say if there's a better solution, when you don't say what it is you need.
Why do each element need a pointer to the list they're stored in?
Anyway, bad things can happen when you inherit from standard containers. They don't have virtual destructors, so you have to be very careful.
A better solution might be to just provide a free function performing the push_back:
template <typename T>
void push_back(std::list<T>& list, const T& t) {
T tmp(t);
tmp.owner = this;
list.push_back(tmp);
}
Apart from avoiding the nonvirtual destructor problem, it also solves your compiler error, because you now only have one type of list.
Of course, if we know why you need this owner pointer in the first place, better still solutions may exist.
Edit
In response to your edit and the comments, use composition, not inheritance:
struct House {
std::string zipcode;
std::list<Person> persons;
void AddPerson(const Person& person) {
Person tmp(person);
tmp.owner = this; // The owner field should be a house, not the list of persons.
persons.push_back(tmp);
}
};
Although I'm not sold on the almost circular references you get when a House stores a list of Persons, and a Person has a pointer to the House it's in.
I'd prefer to decouple these classes as much as possible. If I want to send a letter to a person, I'd call SendLetter(Person, House). Send a letter to this person in that house.
On the side note, you should not extend any classes from std, they are not built for it.
Specifically they don't have virtual destructor so when you call delete on pointer to base class your derived class's destructor will never get called.
You can read more on it Advice on a better way to extend C++ STL container with user-defined methods
I like jalf's free function idea. I'd make it:
template <class X, class Y> // X must have a push_back(Y) member, Y must have an X* owner member
void push_back(X& container, Y value)
{
value.owner = container;
container.push_back(value);
}
This is agnostic over whether the X passed is
a container itself,
is derived from a container as in the original code
or contains a container and has a forwarding push_back function
As it has already been pointed out, the affectation
tmp.owner = this;
fails because this doesn't have the same type as tmp.owner. One solution is to perform a cast, but to do so, you need to provide the container type to BaseList. This can be done using a typedef in Item. Here is the code:
template <class Item> class BaseList
{
public:
void push_back(Item i)
{
i.owner = static_cast<Item::containerType *>(this); // note the cast
items.push_back(i);
}
Item & back() { return items.back(); }
protected:
std::list<Item> items;
};
template <class Container> class Item
{
public:
typedef Container containerType; // Typedef used by BaseList
containerType* owner; // pointer to list that owns us.
};
I also removed the public derivation of std::list: as many said, this is (most of the time) best avoided ; you should consider using composition, or maybe private inheritance.
P.S.: I tried making owner private and BaseList<Item>::push_back friend of Item, but I didn't manage to do it. Is it possible at all? (If too long to answer in comment, feel free to ask a question and answer it)
Regarding const: The type BaseList<MyItem> * const that the compiler mentions is a red herring -- it's not a pointer-to-a-const-object, but a pointer that is const, i.e. an address that won't change. (When you think about it, this never changes to point to something else, does it?)
Consider these classes.
class Base
{
...
};
class Derived : public Base
{
...
};
this function
void BaseFoo( std::vector<Base*>vec )
{
...
}
And finally my vector
std::vector<Derived*>derived;
I want to pass derived to function BaseFoo, but the compiler doesn't let me. How do I solve this, without copying the whole vector to a std::vector<Base*>?
vector<Base*> and vector<Derived*> are unrelated types, so you can't do this. This is explained in the C++ FAQ here.
You need to change your variable from a vector<Derived*> to a vector<Base*> and insert Derived objects into it.
Also, to avoid copying the vector unnecessarily, you should pass it by const-reference, not by value:
void BaseFoo( const std::vector<Base*>& vec )
{
...
}
Finally, to avoid memory leaks, and make your code exception-safe, consider using a container designed to handle heap-allocated objects, e.g:
#include <boost/ptr_container/ptr_vector.hpp>
boost::ptr_vector<Base> vec;
Alternatively, change the vector to hold a smart pointer instead of using raw pointers:
#include <memory>
std::vector< std::shared_ptr<Base*> > vec;
or
#include <boost/shared_ptr.hpp>
std::vector< boost::shared_ptr<Base*> > vec;
In each case, you would need to modify your BaseFoo function accordingly.
Instead of passing the container object (vector<>), pass in begin and end iterators like the rest of the STL algorithms. The function that receives them will be templated, and it won't matter if you pass in Derived* or Base*.
This problem occurs in programming languages that have mutable containers. You cannot pass around a mutable bag of apples as a bag of fruit because you cannot be sure that someone else does not put a lemon into that bag of fruit, after which it no longer qualifies as a bag of apples. If the bag of apples were not mutable, passing it around as a bag of fruit would be fine. Search for covariance/contravariance.
one option is to use a template
template<typename T>
void BaseFoo( const std::vector<T*>& vec)
{
...
}
The drawback is that the implementation has to be in the header and you will get a little code bloat. You will wind up with different functions being instantiated for each type, but the code stays the same. Depending on the use case it's a quick and dirty solution.
Edit, I should note the reason we need a template here is because we are trying to write the same code for unrelated types as noted by several other posters. Templates allow you do solve these exact problems. I also updated it to use a const reference. You should also pass "heavy" objects like a vector by const reference when you don't need a copy, which is basically always.
Generally you would start with a container of base pointers, not the other way.
If you dealing with a third-party library, and this is your only hope, then you can do this:
BaseFoo (*reinterpret_cast<std::vector<Base *> *>(&derived));
Otherwise fix your code with one of the other suggesstions.
Taking Matt Price's answer from above, given that you know in advance what types you want to use with your function, you can declare the function template in the header file, and then add explicit instantiations for those types:
// BaseFoo.h
template<typename T>
void BaseFoo( const std::vector<T*>& vec);
// BaseFoo.cpp
template<typename T>
void BaseFoo( const std::vector<T*>& vec);
{
...
}
// Explicit instantiation means no need for definition in the header file.
template void BaseFoo<Base> ( const std::vector<Base*>& vec );
template void BaseFoo<Derived> ( const std::vector<Derived*>& vec );
If std::vector supported what you're asking for, then it would be possible to defeat the C++ type system without using any casts (edit: ChrisN's link to the C++ FAQ Lite talks about the same issue):
class Base {};
class Derived1 : public Base {};
class Derived2 : public Base {};
void pushStuff(std::vector<Base*>& vec) {
vec.push_back(new Derived2);
vec.push_back(new Base);
}
...
std::vector<Derived1*> vec;
pushStuff(vec); // Not legal
// Now vec contains a Derived2 and a Base!
Since your BaseFoo() function takes the vector by value, it cannot modify the original vector that you passed in, so what I wrote would not be possible. But if it takes a non-const reference and you use reinterpret_cast<std::vector<Base*>&>() to pass your std::vector<Derived*>, you might not get the result that you want, and your program might crash.
Java arrays support covariant subtyping, and this requires Java to do a runtime type check every time you store a value in an array. This too is undesirable.
They are unrelated types -- you can't.