As of C++26, the correct answer to this question is: [`std::polymorphic`](https://cppreference.com/cpp/memory/polymorphic). Another possibility is [`std::variant`](https://en.cppreference.com/cpp/utility/variant).
# Dynamic polymorphism
For dynamic polymorphism, you would make a `std::vector<std::polymorphic<Foo>>`.
[Example](https://compiler-explorer.com/z/bKnoMqr47):
```
#include <algorithm>
#include <iostream>
#include <memory>
#include <vector>
using std::cout;
struct Foo {
virtual void Identify() const { cout << "Foo\n"; }
};
struct Bar : Foo {
void Identify() const override { cout << "Bar\n"; }
};
auto main() -> int
{
auto const foos = std::vector{
std::polymorphic<Foo>{std::in_place_type<Foo>},
std::polymorphic<Foo>{std::in_place_type<Bar>},
};
std::ranges::for_each(foos, [](auto&& f) { f->Identify(); });
}
```
Output:
```text
Foo
Bar
```
## Why is `std::polymorphic` the best answer?
Because `std::polymorphic` provides **value semantics** to dynamically polymorphic types.
What does “value semantics” mean? Very basically, it means that everything works the way you would expect for “regular” types in C++.
* When you copy a `std::polymorphic<base>`, you copy the `base` or `derived` within it.
* If you make a `std::polymorphic<base> const`, you cannot change its (contained) value, and would only be able to call the value’s `const`-qualified member functions.
Also, a `std::polymorphic<base>` is potentially much more efficient than a `std::unique_ptr<base>`, and may not require dynamic allocation at all. (GCC does not yet implement that as of GCC 16. Clang hasn’t implemented `std::polymorphic` at all yet.)
## What can I do before C++26?
If you don’t have `std::polymorphic` yet, you could implement a proxy type that behaves like `std::polymorphic`. Indeed, you might as well just implement your own version of `std::polymorphic`, so that when you *do* get C++26, you can switch to it without issue.
It’s really not that hard to do. You’re basically just wrapping a `std::unique_ptr<base>`. The only trick is how to handle copying, moving, and destruction (if you don’t want to assume `base` has a virtual destructor). The secret is that in addition to the pointer to the object, you also keep a pointer to copy, move, and destroy functions (or a single function that can do all three). When the object is constructed, you know its type, so at that time you can set the copy/move/destroy functions to do the right thing.
Of course, that’s only if you want a *general* solution. If you just want a solution for a specific type (hierarchy), then things can be much simpler. If you know the type (hierarchy) has a `.clone()` function (and a virtual destructor), you can just use that. Or you can make it non-copyable. Whatever works for your use case.
But the easiest thing to do by far would be to just download [the header-only reference implementation of `std::polymorphic`](https://github.com/jbcoe/value_types), and just use that. The main version requires C++20, but there is a second version that works with C++14, if you need it.
## What about using pointers or references?
Using a vector of pointer or reference types works in a pinch, and it was the “usual” solution pre-C++26 for people who didn’t want to bother making proper proxy types… but it has some annoying and dangerous consequences. You need to be *very* careful with them.
Unlike languages like Java, C++ is value-based by default. This is part of the secret to C++’s efficiency; Java, with its reference semantics, can *never* be as fast as C++.
But it’s not just about efficiency. Value semantics are much easier to reason about. Once you start using reference semantics, wacky things can happen, like spooky action at a distance.
[For example… and **don’t do this**, because this is **bad code**](https://compiler-explorer.com/z/EExqM6oqK):
```
#include <algorithm>
#include <memory>
#include <print>
#include <string>
#include <string_view>
#include <vector>
class animal
{
public:
virtual ~animal() = default;
virtual auto set_name(std::string) -> void = 0;
virtual auto name() const -> std::string_view = 0;
};
class dog : public animal
{
std::string _name;
public:
explicit dog(std::string n) : _name{std::move(n)} {}
auto set_name(std::string n) -> void override { _name = std::move(n); }
auto name() const -> std::string_view override { return _name; }
};
auto const make_pets()
{
auto pets = std::vector<std::shared_ptr<animal>>{};
pets.push_back(std::make_shared<dog>("Fido"));
return pets;
}
auto main() -> int
{
auto const pets = make_pets();
std::println("These are my pets:");
std::ranges::for_each(pets, [](auto&& a) { std::println(" {}", a->name()); });
// Now, `pets` is `const`, so it should be impossible to change any of my pets.
//
// To make even doubly sure, I'll make a copy, and leave the original untouched.
auto someone_elses_pets = pets;
someone_elses_pets[0]->set_name("Spot");
// Now I will print my pets again, and they should be unchanged... right?
std::println("These are my pets:");
std::ranges::for_each(pets, [](auto&& a) { std::println(" {}", a->name()); });
}
```
Output:
```text
These are my pets:
Fido
These are my pets:
Spot
```
You *can* use vectors of smart pointers to *fake* vectors of polymorphic types… but you have to be *very, very careful*, and you have to do some extra work, and you still won’t get every feature.
In the particular case of this question, creating a `std::vector<std::unique_ptr<Foo>>`, then filling it with pointers to both `Foo` and `Bar` objects would be UB. That’s because `Foo` does not have a virtual destructor, so [all the `Bar` objects in the vector would not be properly destroyed](https://compiler-explorer.com/z/593WaarxW). (`std::shared_ptr` will work, though. It actually uses the same mechanism that `std::polymorphic` does, though only for destruction, not copying/moving. However, it’s still possible to break even with `std::shared_ptr`… it’s just (usually!) a little more obvious when you do it.)
# Static polymorphism
If you need an *open-ended* set of types, then `std::polymorphic` is the best option. However, it is surprisingly rare that the set of possible objects is actually really open-ended.
Consider a base class like `animal`. The number of possible animals is vast, and new animals are being discovered all the time. You can’t possibly know *every* type of animal you might get.
But now consider something like chess pieces. There are only six types of piece in chess (ignoring [fairy chess](https://en.wikipedia.org/wiki/Fairy_chess)): king, queen, rook, bishop, knight, and pawn. That list is fixed.
You *could* make a `chess::piece` base class, and then have `chess::king`, `chess::queen`, and so on all derive from that. But if you know in advance the entire set of possibilities, then you can do this instead:
```
namespace chess {
class king { /* ... */ |;
class queen { /* ... */ |;
class rook { /* ... */ |;
// ... etc. ...
using piece = std::variant<
king,
queen,
rook,
// ... etc. ...
>;
```
And now to make a polymorphic vector of chess types, you would just need to do: `std::vector<chess::piece>`. Yes. That’s it. And it will work.
[To get polymorphic behaviour, you use `visit()`, or something else from the `std::variant` API (like `std::holds_alternative<T>()`)](https://compiler-explorer.com/z/Y4aYnrz69).
This is *static* polymorphism, rather than *dynamic* polymorphism, and it can be much more efficient (in some cases), and safer (because the set of possibilities is closed).
---
# Errors in the other answer
There are some factual errors, and some *extremely* bad and dangerous bits of advice in [the other answer](https://topanswers.xyz/cplusplus?q=731#a847) that I have to address.
First, it is *very* bad advice to suggest to newbies that they should simply switch out their `std::unique_ptr`s for `std::shared_ptr`s if they get a compiler error. [`std::shared_ptr` is for shared ownership](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#f27-use-a-shared_ptrt-to-share-ownership)… which is something a newbie will basically never want, so they should basically never use `std::shared_ptr`. When I teach, I tell beginners to forget they’ve ever heard of it, because 99.999% of beginner uses of `std::shared_ptr` will be incorrect.
But the *real* problem is that in using `std::shared_ptr` to get around compiler errors with `std::unique_ptr`, they are ignoring that **those errors mean that they are doing something wrong**. The whole reason `std::unique_ptr` was designed to cause these errors was to help catch mistakes and bugs. You should be fixing *those*, not shutting up the errors by doing something *else* wrong (like using a `std::shared_ptr` for non-shared ownership).
In the “bad” example program above that uses a vector of (smart) pointers to polymorphic types, I used `std::shared_ptr`… and that is one of the main reasons for the weird, buggy behaviour. If I had used `std::unique_ptr`, it wouldn’t have compiled, giving you a strong hint that something is wrong. (But of course, it would still be possible to trigger basically the same bug even with `std::unique_ptr`. It would just be a little more complicated to do.
Now, as to the factual errors….
Throughout the answer there is conflation between the *elements*, and *pointers* to the elements. For example:
> As far as `std::vector<std::shared_ptr<Foo>> shared`, this does not have any limitations upon copying elements. Thus RTTI need not be performed because the element itself is copied rather than recreated.
This is complete nonsense.
First, no elements are copied when pointers are copied. When pointers are copied, the pointers are copied, not the elements they point to.
Second, using a shared pointer does *not* magically make elements copyable. A non-copyable element pointed to by a unique pointer remains non-copyable when pointed to by a shared pointer. Again, copying pointers does not copy elements, it just copies pointers.
Using a vector of shared pointers rather than a vector of unique pointers does make the *vector* copyable… **but not the elements**. When you copy a vector of shared pointers, you do not get two copies of the elements, you get two vectors full of pointers to the same original set of elements, and both vectors think they own those elements. Which is generally not good. (Which is why you get an error when you try it with a vector of unique pointers.)
Finally, the comment about RTTI is gibberish. What even does it mean that the elements are copied but not recreated? Is that referring to copy-assignment versus destruction-then-placement-copy-construction? Neither of those make any sense, because you’re just dealing with pointers; you can just swap the pointer, no need for any kind of copy construction/assignment. Either way, RTTI has nothing to do with any of it. Or rather, if you needed RTTI to copy-assign something, you would need RTTI to (destroy-then-placement-)copy-construct it, too (and vice versa). (RTTI *is* involved in the ugly, broken hack below, but that’s an entirely different issue, and it’s neither properly copying nor “recreating” in any case.)
> … elements cannot be copied, so you couldn't do these things with `unique`:
>
> 1. `unique.push_back(new Foo)` instead you'd have to do `unique.emplace_back(new Foo)`
This has nothing to do with copying. Nothing is getting copied there; no elements, and no pointers. (If it *did* have something to do with copying, then wouldn’t `shared.push_back(new Foo)` work? And does it?)
The reason `unique.push_back(new Foo)` doesn’t work is because the `std::unique_ptr` constructor that takes a raw pointer is `explicit`. That means implicit conversions are not allowed. If you do an explicit cast—like `unique.push_back(std::unique_ptr{new Foo})` or `unique.push_back(static_cast<std::unique_ptr<Foo>>(new Foo))`—then it works just fine.
`unique.emplace_back(new Foo)` works because it bypasses the `explicit` protection… which is why some people recommend *not* using `.emplace_back()` unless you really, really mean it, and preferring `.push_back()` in general.
> `*unique.emplace_back(dynamic_cast<Bar*>(unique.front().get()) == nullptr ? new Foo : new Bar) = *unique.front()`
Dear god no. No, no, no, no, no. This is unacceptable. Everything about that line of code is wrong. Wasn’t the whole point here to avoid slicing? Because this is how you get slicing. If you want UB that badly, you might as well just write the original `vector<Foo>` and save some keystrokes. That’s basically just one step removed from what’s happening here.
There is **no possible way** to implement polymorphic copying in the general case. None. Zero. Zip. It **cannot** be done. Ugly and dangerous hacks like the above are a non-solution. The *only* “practical” solution is some kind of ”clone” operation baked into the hierarchy interface… but that is a brittle solution, at best.
`std::polymorphic` achieves polymorphic copying by *cheating*. It knows the real type of whatever you put in it because you have to specify it one way or another to put it in there in the first place. So it doesn’t need any dynamic casting or RTTI or even a vtable entry when it comes time to copy (or move or even destruct). It just needs to remember what you put in.
If you wanted a vector of elements stored by pointer to be copyable—whether those pointers are shared, unique, or raw—you would need some kind of cheat as well. For example, when creating the elements (and pointers) in the first place, take note of their types and store them somewhere (a second, companion vector?), so that when you copy/move/whatever, you can look up what their actual types are, and call the proper copy-assign/construct function. But I mean, if you’re going to do that, you might as well make a smart pointer that keeps track of the true type internally, so you don’t need an external companion vector to keep track. In other words, basically re-implement `std::polymorphic`.