Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to cast a vector of shared_ptrs of a derived class to a vector of share_ptrs of a base class

Tags:

c++

stl

class Interface
{
};

class Class : public Interface
{
};

class Foo
{
public:
    std::vector<std::shared_ptr<Interface>>& GetInterfaces()
    {
        return *(std::vector<std::shared_ptr<Interface>>*)(&m_data);
        //return m_data;
    }

private:
    std::vector<std::shared_ptr<Class>> m_data;
};

This works but is ugly and scary. Is there a better/safer way to do it? I don't want to make m_data of type std::vector<std::shared_ptr<Interface>> because the module Foo belongs to works entirely with Class's, Interface (and Foo::GetInterfaces()) are implemented to interact with a separate module that should only know about the Interface functionality.

Let me know if anything here is unclear, it makes sense to me but I've been banging my head against the problem for a while.

like image 806
Marcos Marin Avatar asked Feb 19 '13 18:02

Marcos Marin


2 Answers

Casting is not correct, they are distinct types; I am pretty certain you are invoking undefined behaviour.

You need to construct a new vector and return it by value.

std::vector<std::shared_ptr<Interface>> b (m_data.begin(), m_data.end());
return b;

This should still be fairly cheap (1 allocation).

like image 157
111111 Avatar answered Oct 07 '22 01:10

111111


Besides that this is not possible with the implementation of vector, the issue is also that references don't convert. Your code is even worse and is undefined behavior.

What you can do is provide an interface that exposes a range or begin/end instead of the container itself. If you combine that with a transform_iterator that does the conversion, you should be set.

Sample code:

class Interface {
  virtual ~Interface();
};

class X : public Interface {};

class C {

private:
  typedef std::shared_ptr<Interface> iptr_type;
  typedef std::shared_ptr<Class> ptr_type;
  std::vector<ptr_type> v_;

  struct F {
    iptr_type 
    operator()(ptr_type p) { return iptr_type(p); }
  };

  typedef boost::transform_iterator<
    F, std::vector<ptr_type>::iterator> iiterator;

public:
  iiterator
  begin()
  { 
    return boost::make_transform_iterator(
      begin(v_), F()); 
  }

  iiterator
  end()
  { 
    return boost::make_transform_iterator(
      end(v_), F());
  }
};
like image 23
pmr Avatar answered Oct 07 '22 01:10

pmr