3

Question1> Why the following code doesn't work?

Question2> What is the correct way to design it?

#include <iostream>
#include <vector>
#include "boost/shared_ptr.hpp"

using namespace std;

class BaseClass
{};

class SubClass : public BaseClass
{};

int main()
{
    std::vector<boost::shared_ptr<SubClass> > vecSubClassShared;

    boost::shared_ptr<SubClass> sub1(new SubClass);

    vecSubClassShared.push_back(sub1);

    // Error    1   error C2440: 'initializing' : cannot convert from 'std::vector<_Ty>' to 'const std::vector<_Ty> &`
    const std::vector<boost::shared_ptr<BaseClass> >& vecBaseShared = vecSubClassShared;
}
q0987
  • 34,938
  • 69
  • 242
  • 387
  • My gut tells me that (if it's possible) it's an ugly solution, given the fact that just casting a single `boost::shared_ptr` to a `boost::shared_ptr` requires [`static_pointer_cast`](http://stackoverflow.com/questions/4764398/how-to-use-boostsmart-ptr-in-polymorphism) – Cornstalks Feb 27 '13 at 05:17
  • I have used a single `boost::shared_ptr` which is initialized with `boost::shared_ptr` and it works as expected. Just curious, why the collection of such things doesn't work. – q0987 Feb 27 '13 at 05:29

2 Answers2

4

In the view of the C++ compiler, shared_ptr<BaseClass> and shared_ptr<DerivedClass> are different, unrelated types. While the compiler knows, that normal Pointers can be converted from Derived* to Base*, it does not know that conceptually the same should apply for shared_ptrs, because it does not know "smart pointers" being pointer-like.

The same applies for vector or for templates in general: two instantiations of a template are two different class, with no relation betwen them (if not designed explicitly). So the compiler sees a vector<Something> bound to a reference to a vector<SomethingElse> which are not related types, so the reference binding fails.

But even if the types were somehow related, the binding would fail:

long l = 5;
int& ir = l; //ERROR
Arne Mertz
  • 24,171
  • 3
  • 51
  • 90
1

shared_ptr<BaseClass> and shared_ptr<SubClass> are distinct, unrelated types, so there's no way for a vector of one to alias a vector of the other, because there's no guarantee that their object layout is the same.

This means that to e.g. pass const std::vector<boost::shared_ptr<BaseClass> >& to a function you'll have to construct a temporary vector of the desired type. You can however do this without incrementing the reference counts on the shared pointers, using shared pointer aliasing:

#include <vector>
#include <memory>

struct B {};
struct D: public B {};

template<typename T, typename U>
std::vector<std::shared_ptr<T>> shared_vector_static_cast(
   const std::vector<std::shared_ptr<U>> &v)
{
   std::vector<std::shared_ptr<T>> w;
   for (const std::shared_ptr<U> &p: v)
      w.push_back(std::shared_ptr<T>(std::shared_ptr<void>(), p.get()));
   return w;
}

int main() {
    std::vector<std::shared_ptr<D>> v{std::make_shared<D>()};
    const std::vector<std::shared_ptr<B>> &w = shared_vector_static_cast<B>(v);
}
ecatmur
  • 152,476
  • 27
  • 293
  • 366
  • There is a bug in the code. The size of w is the end is 2*v.size() rather than v.size(). – q0987 Mar 05 '13 at 15:22