> Firstly, what exactly is the difference between make_unique and plain old unique_ptr?
The make_unique function uses variadic templates to take the arguments to your constructor:
auto oldway = std::unique_ptr<Foo>(new Foo(1, 2));
auto newway = std::make_unique<Foo>(1, 2);
The new way has the small virtue of eliminating one more place where you ever need to call new. However, it also offers the library writer more possibilities in the internal implementation. For instance, they can nest your type in another struct for accounting purposes without copying or additional dereferencing. However the second advantage is more relevant to shared_ptr, which is probably why C++11 had make_shared, but not make_unique (added in C++14).
> I wouldn't be able to use unique_ptr, because that would mean that both the vector and the local scope have pointers to that object.
Yeah, if you're using unique_ptr, the pointer is uniquely owned in at most one place at a time. For your situation, I think the vector should probably own the objects, and you borrow them with references, iterators, or pointers (via the get() method). Be wary of holding on to those references though (the Rust fans will be quick to point out the dangers here).
A fourth option, which you should consider unless your objects are expensive to copy, is to make your objects copyable and not use unique_ptr or shared_ptr at all. I think it's much simpler to reason about objects as values than it is to reason about objects as smart pointers or (dumb) references.
> I ended up using shared_ptr and it seems to be working fine... I think.
Go with what works for you, but shared_ptr makes additional promises about thread safety and interactions with weak_ptr that increase its runtime costs. I wish shared_ptr was simple by default and that there was yet another smart pointer type for when you need those additional features.