Why Are Std::Atomic Objects Not Copyable

Why are std::atomic objects not copyable?

  1. Technical reason: Most atomic types are not guaranteed to be lock-free. The representation of the atomic type might need to contain an embedded mutex and mutexes are not copyable.

  2. Logical reason: What would it mean to copy an atomic type? Would the entire copy operation be expected to be atomic? Would the copy and the original represent the same atomic object?

There is no well-defined meaning for an operation spanning two separately atomic objects that would make this worthwhile. The one thing you can do is transfer the value loaded from one atomic object into another. But the load directly synchronizes only with other operations on the former object, while the store synchronizes with operations on the destination object. And each part can come with completely independent memory ordering constraints.

Spelling out such an operation as a load followed by a store makes that explicit, whereas an assignment would leave one wondering how it relates to the memory access properties of the participating objects. If you insist, you can achieve a similar effect by combining the existing conversions of std::atomic<..> (requires an explicit cast or other intermediate of the value type).

.

Why disabling copy elision for std::atomic doesn't work using C++17?

C++17 doesn't simply say that the previously optional return value optimizations are now mandatory. The actual description of the language changed so that there is no creation of a temporary object anymore in the first place.

So, since C++17, there is no constructor call that could be elided anymore. Hence it makes sense that -fno-elide-constructors doesn't add any temporary creation. That would be against the language rules.

Before C++17 the language describes that a temporary object is created from which the variable is initialized and then adds that a compiler is allowed to elide this temporary. Therefore, whether -fno-elide-constructors is used or not, the compiler is behaving standard compliant by eliding or not eliding the temporary copy.

Why does std::atomicstd::string give trivially copyable error?

std::string cannot be used with std::atomic as it is not TriviallyCopyable

See explanation here:
https://en.cppreference.com/w/cpp/atomic/atomic

The primary std::atomic template may be instantiated with any
TriviallyCopyable type T satisfying both CopyConstructible and
CopyAssignable. The program is ill-formed if any of following values
is false:

https://en.cppreference.com/w/cpp/named_req/TriviallyCopyable

Why is std::mutex neither copyable nor movable?

Expanding a bit on the other answers, basic locks such as Mutexes are the most basic objects in the language design providing atomic operations, lock and unlock here. These might own an OS implemented handle (native_handle) that is a handle for a hardware implemented object, and might even skip the intermediate handle.

Copying such a handle of course, is non-trivial (you can't copy a piece of hardware, and sometimes even a OS handle, trivially). Moving it is potentially worse - move leaves the object in an unspecified state, but by its nature, a mutex is shared across threads. If you gut it on one thread, you would somehow have to inform all other threads - more likely you would just have breaking code. This is a lot of overhead for no potential benefit (I can see).

As to why the move constructor is not explicitly deleted in your reference - no default move constructor is created if there is a (non-default) defined destructor (12.8, comment 9), so there is no need to delete it.

C++11 Thread safety of std::atomicT copy constructors

Is there a reason why std::atomic isn't copy-constructable?

Yes.

When you are asking for a copy constructible atomic, you're asking for the "normal" rules of single-threaded sequential consistency to apply to a variable that doesn't follow those rules.

In essence, there is no generalized solution.

By using the constructor you show in the question, you sacrifice a deterministic outcome in that you have no guarantee that the source and destination objects are equivalent after construction is complete.

If iostream objects are not copyable, why is the following code legal?

See this page. When you say

return file;

file is an "id-expression" (i.e. it's the name of some variable). These have special handling under return:

automatic move from local variables and parameters


If expression is a (possibly parenthesized) id-expression that names a variable ... then overload resolution to select the constructor to use for initialization of the returned value ... is performed twice:

  • first as if expression were an rvalue expression (thus it may select the move constructor), ...
  • then overload resolution is performed as usual, with expression considered as an lvalue (so it may select the copy constructor).

You cannot copy an ifstream, that's true, but you can move it. Therefore, the compiler essentially inserts an implicit std::move for you:

return std::move(file);

which allows the move constructor of ifstream to be called, even though the copy constructor is deleted. "Moving" means that any resources "owned" by the istream are transferred to a new object. The move constructor takes ownership of these resources away from the source object (thus modifying it), while giving it to the new object. Contrast the copy constructor, which we generally suppose should not modify the source and so couldn't take its ownership away. This makes a copy constructor unsafe for istream, since it would mean two objects trying to manage one external resource and possibly confusing each other. A move constructor is not bound by the contract to leave the source object untouched, so it can ensure that resources are only owned by one object.

Error with copy constructor/assignment operator for a class which has std::atomic member variable

You can't copy atomics with a standard copy constructor, since all loads and stores must happen explicitly. You'll have to write your own copy constructor for Sequence which does some initialization of the form value_(rhs.value_.load()) (possibly with more relaxed memory ordering).

Why is a copy constructor being called when passing an atomic into a variadic argument?

Copy Constructors are called in 3 circumstances:

  1. When an object is passed by value
  2. When an object is returned by value
  3. When an object is initialized to the value of an object of the same type.

If you do not want to call the copy constructor, pass the object by reference (preferably constant) to avoid calling the copy constructor.



Related Topics



Leave a reply



Submit