LockedValue implementation I am working on class for simple sharing values between threads without race-conditions, which can't be a std::atomic (this means, no POD and no trivially-copyable types).
The initial intention was to lock a std::function against writing while reading from an other thread.

The class itself is simple; just lock a mutex at every sensitive part of the class, but I am not totally sure if it's correct or not. I think about to lock the mutex in the dtor, but I don't know if it's good style or not.

#pragma once

template &lt;class T&gt;
class LockedValue
{
private:
    using Lock = std::scoped_lock&lt;std::mutex&gt;;

public:
    LockedValue() = default;

    LockedValue(const LockedValue&amp; _other) :
        m_Value(_other.load())
    {}

    LockedValue(LockedValue&amp; _other) :
        LockedValue(static_cast&lt;const LockedValue&amp;&gt;(_other))
    {}

    LockedValue(LockedValue&amp;&amp; _other) :
        m_Value(_other._takeValue())
    {}

    template &lt;class... Args&gt;
    LockedValue(Args&amp;&amp;... _args) :
        m_Value(std::forward&lt;Args&gt;(_args)...)
    {}

    LockedValue&amp; operator =(const LockedValue&amp; _other)
    {
        auto value = _other.load();
        store(std::move(value));
        return *this;
    }

    LockedValue&amp; operator =(LockedValue&amp;&amp; _other)
    {
        if (this != &amp;_other)
            store(_other._takeValue());
        return *this;
    }

    template &lt;class T_, typename = std::enable_if_t&lt;std::is_convertible_v&lt;T_, T&gt;&gt;&gt;
    LockedValue&amp; operator =(T_&amp;&amp; _value)
    {
        store(std::forward&lt;T_&gt;(_value));
        return *this;
    }

    T operator *() const
    {
        return load();
    }

    template &lt;class T_&gt;
    void store(T_&amp;&amp; _value)
    {
        Lock lock(m_Mutex);
        m_Value = std::forward&lt;T_&gt;(_value);
    }

    T load() const
    {
        Lock lock(m_Mutex);
        return m_Value;
    }

private:
    mutable std::mutex m_Mutex;
    T m_Value;

    T&amp;&amp; _takeValue()
    {
        Lock lock(m_Mutex);
        return std::move(m_Value);
    }
};


EDIT: Is it necessary to check for self-moving in move assign? I think it should be ok to remove the self check.

EDIT2: To the copy ctor related discussion. Delete the non-const&amp; copy ctor from the class and try to use an object like this.

If you don't have c++17 change the Lock alias to std::lock_guard and change std::is_convertible_v to std::is_convertible::value

I pasted it at this online compiler with the ctor already deactivated
click me

// calls the variadic ctor
LockedValue&lt;std::string&gt; s("test");

// this is fine; calls the usual copty ctor
auto u(static_cast&lt;const LockedValue&lt;std::string&gt;&amp;&gt;(s)); 

/* results in an error because type LockedValue&lt;std::string&gt;&amp;
can not be converted to std::string; this means a second call
for the variadic ctor*/
auto t(s);
You don't need both version of the copy constructor.

    LockedValue(const LockedValue&amp; _other) :
        m_Value(_other.load())
    {}

    LockedValue(LockedValue&amp; _other) :
        LockedValue(static_cast&lt;const LockedValue&amp;&gt;(_other))
    {}


If this is a mutating copy constructor. Then just have the second one. Otherwise have the first one. Since the mutating one simple calls the const version I see no reason for the second one as a non const object will still bind to the const version.

Move operations are usually noexcept. I suppose your are potentially throwing. I would need to look up all the funtions called. You should check and if your move operations are non throwing then you need to make an appropriate comment.

    LockedValue(LockedValue&amp;&amp; _other) :
        m_Value(_other._takeValue())
    {}


A standard move is simply a swap. I suppose you would need a safe swap. Which would be doubly hard as you run into race conditions. But there is always std::lock to help you with that (But its an extra step).

    LockedValue&amp; operator =(LockedValue&amp;&amp; _other)
    {
        if (this != &amp;_other)
            store(_other._takeValue());
        return *this;
    }


Thay's nice. I think I learned a new thing for the day.

    template &lt;class T_, typename = std::enable_if_t&lt;std::is_convertible_v&lt;T_, T&gt;&gt;&gt;
    LockedValue&amp; operator =(T_&amp;&amp; _value)
    {
        store(std::forward&lt;T_&gt;(_value));
        return *this;
    }


And a correct usage of the mutable keyword.

    mutable std::mutex m_Mutex;