Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Achieving constant observance with std::unique_ptr

See first the problem that I’m having, and then my use case (it’s possible that I’m misusing std::unique_ptr, so I wanted to include a note on what I’m actually trying to do).

My Problem

I’m trying to find a way to support observance into a std::unique_ptr. That is, have a non-owning function that will take in the contents of the std::unique_ptr, but won’t be able to modify any of its components. The intuitive pattern for this would be to write a function taking a const T* object, and then calling that function with MyUniquePtr.get(). But this allows for the following problematic snippet:

#include <memory>

struct unique_wrapper
{
    unique_wrapper(std::unique_ptr<int>&& m) : m(std::move(m))
    {}
    std::unique_ptr<int> m;
};

void foo(const unique_wrapper* w)
{

    // PROBLEM CODE
    *(w->m) = 5;
}

int main()
{
    auto unique_squared = std::make_unique<unique_wrapper>(std::make_unique<int>(0));
    foo(unique_squared.get());
}

The above snippet really scares me, because we completely violate the guarantee of a std::unique_ptr about having a single owner.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

And so this leads to my question: what is the natural way to share data in C++ while offering a guarantee that one party won’t modify it?

My Use Case

I have a data structure that should have a single owner, but we want to pass this single object to several observing parties that will not take ownership of it (as the owner still has work to do with it), but certainly need to access its contents

auto object = acquire_resources();

// don't want to transfer object ownership!
validate_object_state(object);

// use object further
...

>Solution :

Your problem is const-correctness. A class that exposes a pointer (including a smart pointer) is not const-correct. Make it const-correct by making m private and exposing operator* and operator-> instead:

struct unique_wrapper {
    unique_wrapper(std::unique_ptr<int>&& m) : m(std::move(m)) {}

    // add non-const versions only if unique_wrapper is meant to modify data as non-const
    int& operator*() noexcept { return *m; }
    const int& operator*() const noexcept { return *m; }

    // operator-> is not very useful for pointer of int, but I guess your real problem is more complex
    int* operator->() noexcept { return m.get(); }
    const int* operator->() const noexcept { return m.get(); }

private:
    std::unique_ptr<int> m;
};

void foo(const unique_wrapper* w)
{
    **w = 5; // error, it's const
}

void bar(unique_wrapper* w)
{
    **w = 5; // fine, can modify
}

See it online

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading