Type wrapper that performs value-initialization on copy construction or assignment.
Rather than copying the source supplied for copy construction or copy assignment, this wrapper instead value-initializes the destination object. Move assignment and construction preserve contents in the destination as usual, but reset the source to its value-initialized value.
Only types T that satisfy std::is_scalar<T>
are currently permitted: integral and floating point types, enums, and pointers. Value initialization means the initialization performed when a variable is constructed with an empty initializer {}
. For the restricted set of types we support, that just means that numeric types are set to zero and pointer types are set to nullptr. Also, all the methods here are noexcept due to the std::is_scalar<T>
restriction. See http://en.cppreference.com/w/cpp/language/value_initialization.
Background:
It is preferable to use default copy construction for classes whenever possible because it avoids difficult-to-maintain enumeration of member fields in bespoke copy constructors. The presence of fields that must be reset to zero in the copy (counters, for example) prevents use of default copy construction. Similarly, pointers that would be invalid in the copy need to be set to null to avoid stale references. By wrapping those problematic data members in this adapter, default copy construction can continue to be used, with all data members copied properly except the designated ones, which are value-initialized instead. The resetting of the source on move doesn't change semantics since the condition of the source after a move is generally undefined. It is instead opportunistic good hygiene for early detection of bugs, taking advantage of the fact that we know type T can be value-initialized. See reset_after_move for more discussion.
Example:
class Foo { public: DRAKE_DEFAULT_COPY_AND_MOVE_AND_ASSIGN(Foo); Foo() = default;
private: std::vector<int> items_; reset_on_copy<int> use_count_; };
When copying from Foo
, the new object will contain a copy of items_
but use_count_
will be zero. If Foo
had not used the reset_on_copy
wrapper, use_count_
would have been copied also, which we're assuming is not the desired behavior here.
reset_on_copy<int> some_member_{5}
it will be reset to zero, not reinitialized to 5 when copied.T | must satisfy std::is_scalar<T> . |
#include <drake/common/reset_on_copy.h>
Public Member Functions | |
reset_on_copy () noexcept(std::is_nothrow_default_constructible_v< T >) | |
Constructs a reset_on_copy<T> with a value-initialized wrapped value. More... | |
reset_on_copy (const T &value) noexcept(std::is_nothrow_copy_constructible_v< T >) | |
Constructs a reset_on_copy<T> with a copy of the given value. More... | |
reset_on_copy (T &&value) noexcept(std::is_nothrow_move_constructible_v< T >) | |
Constructs a reset_on_copy<T> with the given wrapped value, by move construction if possible. More... | |
Implements copy/move construction and assignment. | |
These make reset_on_copy objects CopyConstructible, CopyAssignable, MoveConstructible, and MoveAssignable. | |
reset_on_copy (const reset_on_copy &) noexcept(std::is_nothrow_default_constructible_v< T >) | |
Copy constructor just value-initializes instead; the source is ignored. More... | |
reset_on_copy & | operator= (const reset_on_copy &source) noexcept(std::is_nothrow_destructible_v< T > &&std::is_nothrow_default_constructible_v< T >) |
Copy assignment just destructs the contained value and then value-initializes it, except for self-assignment which does nothing. More... | |
reset_on_copy (reset_on_copy &&source) noexcept(std::is_nothrow_move_constructible_v< T > &&std::is_nothrow_destructible_v< T > &&std::is_nothrow_default_constructible_v< T >) | |
Move construction uses T's move constructor, then destructs and value initializes the source. More... | |
reset_on_copy & | operator= (reset_on_copy &&source) noexcept(std::is_nothrow_move_assignable_v< T > &&std::is_nothrow_destructible_v< T > &&std::is_nothrow_default_constructible_v< T >) |
Move assignment uses T's move assignment, then destructs and value initializes the source, except for self-assignment which does nothing. More... | |
Implicit conversion operators to make reset_on_copy<T> act | |
as the wrapped type. | |
operator T & () noexcept | |
operator const T & () const noexcept | |
Dereference operators if T is a pointer type. | |
If type T is a pointer, these exist and return the pointed-to object. For non-pointer types these methods are not instantiated. | |
template<typename T1 = T> | |
std::enable_if_t< std::is_pointer_v< T1 >, T > | operator-> () const noexcept |
template<typename T1 = T> | |
std::enable_if_t< std::is_pointer_v< T1 >, std::add_lvalue_reference_t< std::remove_pointer_t< T > > > | operator * () const noexcept |
|
noexcept |
Constructs a reset_on_copy<T> with a value-initialized wrapped value.
|
noexcept |
Constructs a reset_on_copy<T> with a copy of the given value.
This is an implicit conversion, so that reset_on_copy<T> behaves more like the unwrapped type.
|
noexcept |
Constructs a reset_on_copy<T> with the given wrapped value, by move construction if possible.
This is an implicit conversion, so that reset_on_copy<T> behaves more like the unwrapped type.
|
noexcept |
Copy constructor just value-initializes instead; the source is ignored.
|
noexcept |
Move construction uses T's move constructor, then destructs and value initializes the source.
|
noexcept |
|
noexcept |
|
noexcept |
|
noexcept |
|
noexcept |
Copy assignment just destructs the contained value and then value-initializes it, except for self-assignment which does nothing.
The source argument is otherwise ignored.
|
noexcept |
Move assignment uses T's move assignment, then destructs and value initializes the source, except for self-assignment which does nothing.
The source argument is otherwise ignored.