Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A type that can hold both Foo* and std::shared_ptr<Foo>

Tags:

c++

c++17

What's the most convenient way to be able to store either std::shared_ptr or Foo* in the same type?

Foo* a = ...;
std::shared_ptr<Foo> b = ...;

Bar c = a; // Success, Bar type can hold Foo*
Bar d = b; // Success, Bar type can also hold std::shared_ptr<Foo>

std::variant<Foo*, std::shared_ptr< Foo>> is okay, but it's not possible to dereference it directly and that is kind of annoying. Is there a better way?

like image 956
Vladimir Bogachev Avatar asked Jan 12 '21 15:01

Vladimir Bogachev


People also ask

What is the difference between Make_shared and shared_ptr?

The difference is that std::make_shared performs one heap-allocation, whereas calling the std::shared_ptr constructor performs two.

What is shared pointer in c++?

The shared_ptr type is a smart pointer in the C++ standard library that is designed for scenarios in which more than one owner might have to manage the lifetime of the object in memory.

How to initialize shared_ ptr in constructor?

Here is an example of how we create a shared_ptr that is empty but still pointing to an object: int x = 100; //'px' holds &x, but is empty. //A null and empty shared_ptr<void> is passed //to aliasing constructor to initialize px std::shared_ptr<int> px(std::shared_ptr<void>(), &x);

What is std :: Make_shared?

std::make_sharedConstructs an object of type T and wraps it in a std::shared_ptr using args as the parameter list for the constructor of T .

What is shared_ptr in C++?

(since C++11) std::shared_ptr is a smart pointer that retains shared ownership of an object through a pointer. Several shared_ptr objects may own the same object. The object is destroyed and its memory deallocated when either of the following happens:

Why do we use shared_ptr for user-data?

Having an std::shared_ptr as the user-data pointer, as opposed to a raw pointer, is safer because it makes it easier to manage the lifecycle of the associated user-data or context. One way to use std::shared_ptr for user-data is to define an interface that clients can inherit.

Is the Deleter of a shared_ptr type-erased?

Generally, it is said that the deleter of an std::shared_ptr is type-erased. To achieve this type-erasure, the std::shared_ptr stores the deleter as part of its control block.

How does the destructor of shared_ptr work in C++?

The destructor of shared_ptr decrements the number of shared owners of the control block. If that counter reaches zero, the control block calls the destructor of the managed object. The control block does not deallocate itself until the std::weak_ptr counter reaches zero as well.


Video Answer


2 Answers

Just use a std::shared_ptr<Foo>.

While it is rarely useful, you can in fact construct a non-owning non-counting std::shared_ptr:

auto p = std::shared_ptr<Foo>{std::shared_ptr<void>(), raw_pointer};

If you want to cater to weird people disrespecting the abstraction (looking at the reference-counts, to be specific), you could also stash an eternal anchor somewhere and use that:

struct pass_t {
    template <class... T>
    constexpr int operator()(T&&...) noexcept
    { return 0; }
};
constexpr inline pass_t pass;

inline const std::shared_ptr<void> anchor {nullptr, pass};

auto p = std::shared_ptr<Foo>{anchor, raw_pointer};
like image 92
Deduplicator Avatar answered Oct 19 '22 03:10

Deduplicator


Basically the same as Caleth's, but more generic and exposing the variant nature of that new type:

template <class... PointerTypes>
struct PointerHolder : std::variant<PointerTypes...> {
    using std::variant<PointerTypes...>::variant;
    
    auto *operator -> () const {
        return &operator*();
    }
    
    auto &operator * () const {
        return std::visit([](auto const &p) -> auto & { return *p; }, *this);
    }
};

See it live on Wandbox

like image 3
Quentin Avatar answered Oct 19 '22 02:10

Quentin