17

I'm trying to figure out if it is possible to create an array of shared pointers to different types. For example, something like that:

vector<shared_ptr<**???**>> v;
v.push_back(shared_ptr<int>(new int));
v.push_back(shared_ptr<MyClass>(new MyClass()));

or any other way to pass shared_ptr without knowing its type.

  • Did you try to get a pointer from that vector? How do you want to detect the type cast you will need? -- Make all items in the vector inherited from a base class like "IObject". In that case you know the type of the vector and C++ allows you to safely cast. – harper Nov 10 '14 at 11:45
  • Yes, but you ask for an array of shared_ptr<>. A pair is something different and might be useful. – harper Nov 10 '14 at 13:51
  • 4
    You do not want to do that. (Instead of asking _"How do I do X in C++?"_, explain what your underlying problem is, and it's likely someone will come up with a solution that prevents you from doing this. – sbi Nov 19 '14 at 12:11
  • 1
    Try to use `std::make_shared` as it is more efficient. – Neil Kirk Nov 19 '14 at 16:44

3 Answers3

27

More type safe might be:

/// header #include <boost/variant.hpp>
typedef boost::variant < boost::shared_ptr < T1 >, boost::shared_ptr < T2 >, ... > VariantT;

std::vector < VariantT > container;
container.push_back ( boost::shared_ptr < T1 > ( new T1 ) ); // or boost::make_shared
container.push_back ( boost::shared_ptr < T2 > ( new T2 ) ); // or boost::make_shared

More take a look at the Boost.Variant library.

Sardathrion - against SE abuse
  • 17,269
  • 27
  • 101
  • 156
sfrehse
  • 1,062
  • 9
  • 22
  • 4
    Note that this approach only works provided you can constrain the types the variant supports at compile-time. – bpw1621 Nov 19 '14 at 13:50
  • 4
    @bpw: How would you do otherwise? You cannot cast (and this always boils down to casting at some level or other) to a type you do not know at compile-time. The only way I can think of to deal with types unknown at compile-time is runtime polymorphism, which requires related types. – sbi Nov 20 '14 at 11:39
22

Assuming you want to store objects that do not inherit a common class, the simplest way that comes to mind is to use boost::any.

You still have to be sure about what's the type of each object stored at each index (i.e. to be able to perform the correct boost::any_cast).

You should do that rather than storing pointers to void. This is the closest as it gets to the semantically correct way of storing "something you know the type but the compiler doesn't", implying a cast when retrieving the value.

While both (any and a void pointer) will work the same way (pointer aside), if you cast to the wrong type, any will throw an bad_any_cast exception (IIRC), while with the void pointer, you'll get undefined behavior. A simple try on coliru yielded a segfault.

JBL
  • 12,588
  • 4
  • 53
  • 84
  • Thanks. So there is no way of doing what I want with only c++11 tools? –  Nov 10 '14 at 11:49
  • 2
    Well, you could try to implement a solution for that, but I think it will end up very much like `boost::any` anyway. Oh and `boost::any` is a header-only boost feature, so there's not even the argument of having to link to an extra lib. – JBL Nov 10 '14 at 12:41
  • 6
    Actually, what you'll get its undefined behavior. That shows as a segfault _if you're lucky_. – sbi Nov 19 '14 at 19:04
  • 5
    `boost::any` is not a class template, it is a class. – Maxim Egorushkin Nov 20 '14 at 11:46
  • @MaximYegorushkin Right! – JBL Nov 20 '14 at 13:52
5

Just as any fundamental pointer type is convertible to void*, any shared_ptr will convert to shared_ptr<void>:

vector<shared_ptr<void>> v;
v.push_back(make_shared<int>());
v.push_back(make_shared<MyClass>());

You now have a vector of fully type-erased shared pointers. You can't do much of anything interesting with it, however: you'd have to somehow know what types are stored in what slots of the vector to convert them back:

auto intptr = static_pointer_cast<int>(v[0]);
auto myclass_ptr = static_pointer_cast<MyClass>(v[1]);

So although it is possible to do this, it's not terribly useful and probably an indication of a broken design.

Casey
  • 41,449
  • 7
  • 95
  • 125
  • 21
    No, this isn't what you are looking for. Genericity through `void*` is bad and will lead you to write code that ___buggy, hard to fix, and hard to maintain___. Many of C++' constructs were invented exactly to get rid of this old C habit. [sfrehse's answer](http://stackoverflow.com/a/26844776/140719) is way better. – sbi Nov 19 '14 at 12:10
  • 3
    Well, I learned something here. Didn't know that trick was possible. Of course I now have to make sure I never, ever do something that daft in my own code. Satus - please listen to everyone letting you know this is a broken design. You're mixing idiomatic C with idiomatic C++ and the only result can be fragile code. – Rob G Nov 19 '14 at 15:45
  • 3
    @Satus - By removing type information entirely (in the `void*` solution), you lose safety, expressiveness, and performance. Safety because there is no check that you pull out the right type. A `variant` or `any` class is a *discriminated* union, which is to say, it *remembers and checks* its type. Expressiveness is lost because it could be *ANYTHING*, and that's dangerous and not self-documenting. Efficiency is lost because the compiler can no longer optimize based on type information. A discriminated union isn't as good as an actual type, but it's better than `void*`. Don't use this solution! – Mark Nov 19 '14 at 18:41
  • 2
    @Satus: I say _"You don't want to do that because it leads to code that's buggy, hard to fix, and hard to maintain."_ and you ask why you don't want to do this? I'm not sure what to answer to that. – sbi Nov 19 '14 at 19:08
  • 3
    @Satus - That's actually just entirely not true. I teach, I see this in office hours, latent bugs that present non-deterministically because sometimes the garbage you get is just close enough to what you needed that it works. You *might* catch it on the first run if you ran the program with valgrind. I have yet to see any substance in your commitment to this lousy approach, only laziness. – Mark Nov 20 '14 at 06:58
  • I couldn't possibly agree more with @Mark's comment. See [here](http://stackoverflow.com/a/1553407/140719) for what _Undefined Behavior_ means. – sbi Nov 20 '14 at 11:47