I find it pretty annoying that c++11 atomics can't be copied. The reasons for this have been discussed e.g. here and I don't want to argue about them now. However, I find myself repeatedly in situations, where I want to copy data structures around that contain atomics. Usually BEFORE they are actually used in a multithreaded context (e.g. in order to return them from a factory function, or to store them in a vector etc.).
In order to solve that problem without having to manually write a copy constructor over and over again, I decided to write a simple class, that publicly derives from std::atomic
and adds those functionality:
/**
* Drop in replacement for std::atomic that provides a copy constructor and copy assignment operator.
*
* Contrary to normal atomics, these atomics don't prevent the generation of
* default constructor and copy operators for classes they are members of.
*
* Copying those atomics is thread safe, but be aware that
* it doesn't provide any form of synchronization.
*/
template<class T>
class CopyableAtomic : public std::atomic<T>
{
public:
//defaultinitializes value
CopyableAtomic() :
std::atomic<T>(T{})
{}
constexpr CopyableAtomic(T desired) :
std::atomic<T>(desired)
{}
constexpr CopyableAtomic(const CopyableAtomic<T>& other) :
CopyableAtomic(other.load(std::memory_order_relaxed))
{}
CopyableAtomic& operator=(const CopyableAtomic<T>& other) {
this->store(other.load(std::memory_order_relaxed), std::memory_order_relaxed);
return *this;
}
};
In my toy examples this worked pretty well, however, I'm not sure, if I really considered all possible ramifications of this - deriving from standard library types feels awkward enough and when it comes to synchronization primitives I feel a bit like playing with fire (or juggling with razorblades to quote Herb Sutter).
So what I would like to know (aside from general improvement suggestions, or alternative approaches):
- Can this really serve as a drop-in replacement everywhere, where you would use a normal atomic (especially also with CAS instructions).
- Is my claim, that the copy constructor / assignment operator is threadsafe correct (both with respect to
this
as well as with respect toother
) - Can you think of any performance regressions that this would introduce when used as a synchronization primitive instead of a std::atomic (e.g. because - for some reasons - compilers treat std::atomics in a way they don't / can treat this class)
swap
(as a friend function) so that it uses an atomic exchange ? \$\endgroup\$