9 [class] / 5 A union is a class defined with the keywordunion
; at the same time he can keep only one of his members (9.5). [...]
9.5 [class.union] / 1 Only one of the non-static members can be active in the union, that is, at the moment, the value of only one of its non-static members can be stored in the association. [...] The size of the union is sufficient to contain the largest of its non-static members. Each non-static member is located in memory as if it is the only member of the structure. All non-static members of the union object have the same address.
9 [class] / 5 A union is a class defined with the class-key union; it holds at a time (9.5). [...]
9.5 [class.union] / In union data data data data data union at any time. [...] non-static data members. Each member of a struct. All non-static data is the same as address.
3.8 [basic.life] / 1 [...] The lifetime of an object of typeT
begins when:
- storage obtained with type
T
alignment and size and- object initialization, if it is non-trivial, completed.
The lifetime of an object of typeT
ends when:
- if
T
is a type of a class with a non-trivial destructor (12.4), the call to the destructor begins, or- the repository that the object occupies has been reused or released.
3.8 [basic.life] / 1 [...]T
begins when:
- size is obtained, and
- if the object has a non-trivial initialization, its initialization is complete.
T
ends when:
- if
T
is a class type with a non-trivial destructor (12.4), the destructor call starts, or- The storage of the object occupies is reused or released.
9.5 [class.union] / 1 [...] A class object with a non-trivial constructor (12.1), a non-trivial copy constructor (12.8), a non-trivial destructor (12.4) or a non-trivial copy assignment operator (13.5.3, 12.8) cannot be a member union, or an element of the array lying in the union. [...]
9.5 [class.union] / 1 [...] An object of a non-trivial constructor (12.1), a non-trivial copy constructor (12.8), a non-trivial destructor (12.4), or a non -trivial copy assignment operator (13.5.3, 12.8) cannot be a member of a union, nor can it be an array of such objects. [...]
new
to construct a new active member.9.5 [class.union] / 4 [Note: in general, it is necessary to use one explicit call to the destructor and one explicit call to the placing operatornew
to change the active member of the enumeration. —End of note] [Example: Consider an objectu
, having the type of enumerationU
with non-static membersm
typeM
andn
typeN
IfM
has a non-trivial destructor, andN
has a non-trivial constructor (for example, if they declare or inherit virtual functions), the active memberu
can be safely changed fromm
ton
by using the following destructor and placingnew
operator:
um~M(); new (&u.n) N;
—The end of the example]
9.5 [class.union] / 4 [note: In general, it should be noted . —End note] [Example: Consider a non-static data type and have a non-static data members and an
of typeN
If you have a non-trivial constructor (for example, if you declare or inherit virtual functions), follows:
um~M(); new (&u.n) N;
—End example]
9.5 [class.union] / 2 [Note: if any non-static member of the union has a non-trivial default constructor (12.1), a copy constructor (12.8), a displacement constructor (12.8), a copy assignment operator (12.8), a move assignment operator (12.8) or destructor (12.4), the corresponding member function of the union must be provided by the user, or it will be implicitly removed (8.4.3) from the union. —End of note]
9.5 [class.union] / 3 [Example: Consider the following union:
union U { int i; float f; std::string s; };
Since the typestd::string
(21.3) declares non-trivial versions of all special member functions, typeU
will implicitly remove the default constructor, the copy / move constructors, the copy / move assignment operators, and the destructor. To use typeU
some of these member functions must be provided by the user. —The end of the example]
9.5 [class.union] / 2 (non-trivial default constructor (12.1), copy constructor (12.8), move constructor (12.8), copy assignment operator (12.8) , move assignment operator (12.8), or delete it (8.4.3) for the union. —End note]
9.5 [class.union] / 3 [Example: Consider the following union:
union U { int i; float f; std::string s; };
Since he has been an implicitly defaulted constructor, he has been defined as a constructor To use it, it must be user-provided. —End example]
eggs::variant<Ts...>
is a marked union of objects with types Ts
. It provides a natural interface for switching the active member and provides all the special member functions with their usual semantics: eggs::variants<N, M> u; // u u = M{}; // u M u = N{}; // u N, // - using U = eggs::variants<int, float, std::string>;
struct U { union { T0 m0; ...; TN mN; }; std::size_t which; } u;
using V = eggs::variant<T0, ..., TN>; V v;
V
must match the size of the corresponding type U
Any active member v
must be located in the domain V
, properly aligned for the types T0, ... TN
; the use of additional storage, such as dynamic memory, is not allowed.v
must match or improve the well-defined semantics of u
. The v
interface should not allow undefined behavior, for example, a reference to the inactive member u
.V
should provide all special member functions with their expected semantics.std::experimental::optional<T>
, as defined in the Technical Specification of the main library . The conceptual model optional<T>
consists of a tagged union of the types nullopt_t
and T
Design decisions made for optional<T>
are easily transferred to the variant<Ts...>
, whose conceptual model consists of the marked-up union of the nullvariant_t
types and those that are hidden behind Ts
. The semantics of all special member functions and related operators, as well as the interface for switching the active member — through design, assignment, or placement — is borrowed from optional<T>
.std::function
, which returns a pointer to the target if it is requested with the correct target type — something like dynamic_cast
for the poor. In addition, it also allows you to get a null pointer to the active member (if there is one), which has been very useful to simplify the implementation of auxiliary functions.std::tuple
provided, as well as access to elements by index or type — albeit with unobvious semantics closer to the semantics of casting at run time.variant<Ts>
as the storage would use the underlying relaxed union : template <typename ...Ts> union storage { nullvariant_t nullvariant; Ts... members; };
template <typename ...Ts> union storage; template <typename T, typename ...Ts> union storage<T, Ts...> { nullvariant_t nullvariant; T head; storage<Ts...> tail; }; template <> union storage<> { nullvariant_t nullvariant; };
Ts
as a result, remove the corresponding special member function from the repository. To use this implementation, at least the default constructor and destructor must be implemented for the resulting list, although the destructor cannot do anything useful.Ts
- attention, spoiler: in some cases it will not work. The standard even provides a special property to facilitate our work:10.20.7.6 [meta.trans.other]
template <std::size_t Len, class... Types> struct aligned_union;
- Condition: At least one type must be provided.
- Comments: The typedef to the member type must be a POD type, applicable for use as an uninitialized repository for any object whose type is listed in the
Types
list; its size must be at leastLen
. The static memberalignment_value
must be an integer constant of typestd::size_t
, whose value determines the strictest alignment for all types listed in theTypes
list.
10.20.7.6 [meta.trans.other]
template <std::size_t Len, class... Types> struct aligned_union;
- Condition: At least one type is provided.
- This is the case: its size shall be at least
Len
. This is the case for the quotation.
template <std::size_t Len, typename ...Types> struct aligned_union { static constexpr std::size_t alignment_value = std::max({alignof(Types)...}); struct type { alignas(alignment_value) unsigned char _[std::max({Len, sizeof(Types)...})]; }; };
aligned_union
as the storage type, a simplified version of variant<Ts>
can be implemented as follows: template <typename ...Ts> class variant { template <typename T> struct _index_of { /*...*/ }; // T Ts..., 0 public: static constexpr std::size_t npos = std::size_t(-1); variant() noexcept : _which{npos} {} template <typename T> variant(T const& v) : _which{_index_of<T>::value} { new (target<T>()) T(v); // T // new } /*...*/ std::size_t which() const noexcept { return _which; } template <typename T> T* target() noexcept { return _which == _index_of<T>::value ? static_cast<T*>(static_cast<void*>(&_storage)) : nullptr; } template <typename T> T const* target() const noexcept { return _which == _index_of<T>::value ? static_cast<T const*>(static_cast<void const*>(&_storage)) : nullptr; } private: std::size_t _which; typename std::aligned_union<0, Ts...>::type _storage; };
switch
to achieve this goal — although if we could do this, it would hardly have been much simpler — and the direct replacement would have a recursive implementation: struct _destructor { template <typename T> static void call(void* ptr) { static_cast<T*>(ptr)->~T(); } }; variant<Ts...>::~variant() { apply<_destructor, Ts...>(_which, &_storage); } template <typename F> void apply(std::size_t /*which*/, void* /*storage*/) {} template <typename F, typename T, typename ...Ts> void apply(std::size_t which, void* storage) { if (which == 0) { F::template call<T>(storage); } else { apply<F, Ts...>(which - 1, storage); } }
apply
method can be constructed using a jump table, just as a switch
does, with a subsequent transition to the appropriate entry: template <typename F, typename ...Ts> void apply(std::size_t which, void* storage) { using fun_ptr = void(*)(void*); static constexpr fun_ptr table[] = {&F::template call<Ts>...}; if (which < sizeof...(Ts)) { table[which](storage); } }
Ts
list - in your case, the estimate may change.std::memcpy
.3.9 [basic.types] / 2 For any object (except subobjects of the base class) of the trivially copied typeT
, whether or not they contain valid values ​​of typeT
, its constituent bytes (1.7) can be copied to thechar
array orunsigned char
. If the contents of thechar
array orunsigned char
copied back to an object, the object should receive its original value as a result. [...]
3.9 [basic.types] / 3 For any trivially copied typeT
, if two pointers toT
point to different objectsobj1
andobj2
typeT
, where eitherobj1
orobj2
are subobjects of the base class and the componentsobj1
bytes (1.7) are copied intoobj2
, as a result,obj2
must contain the same value asobj1
. [...]
3.9 basic basic basic 2 2 2 2 2 2 2 2 2 any any be copied into an array ofchar
orunsigned char
. It is a charcoal pattern that holds its original value. [...]
3.9 [basic.types] / 3, for any trivially copyable typeT
,obj1
andobj2
, what’s not a norm of theobj2
a ob (1.7) making upobj1
are copied intoobj2
,obj2
. [...]
variant
with a trivially copied type must also strive to be trivially replicable.9 [class] / 6 A trivially copied class is a class that:
- does not have non-trivial copy constructors (12.8),
- does not have non-trivial displacement constructors (12.8),
- does not have non-trivial copy assignment operators (13.5.3, 12.8),
- does not have nontrivial relocation assignment operators (13.5.3, 12.8) and
- has a trivial destructor (12.4).
[...]
9 [class] / 6 A trivially copyable class is a class that:
- has no non-trivial copy constructors (12.8),
- has no non-trivial move constructors (12.8),
- has no non-trivial copy assignment operators (13.5.3, 12.8),
- has no non-trivial move assignment operators (13.5.3, 12.8), and
- has a trivial destructor (12.4).
[...]
variant
specialization must be chosen if all types in Ts
are trivially replicable. The special member functions listed above should not be provided by the user for this specialization — they must either be provided implicitly or explicitly specified when they are first defined as generated by default. The implementation will provide implicit definitions for them, which will be trivial; copy and move operators will simply copy the storage bits containing them together with the discriminator, but the destructor will not do anything.boost::noncopyable
defined: class noncopyable { protected: constexpr noncopyable() = default; noncopyable(noncopyable const&) = delete; noncopyable& operator=(noncopyable const&) = delete; ~noncopyable() = default; };
std::is_trivially_copyable
prints true
for the noncopyable
class. Another big surprise is that the variant<noncopyable>
instance variant<noncopyable>
can be successfully copied, since the remote noncopyable
member functions are not used at all. This is, in effect, a type security breach arising from the decision to use an untyped bare repository to store the active member.12.4 [class.dtor] / 5 [...] A destructor is trivial if it is not provided by the user and if:
- it is not virtual,
- all immediate base classes of this class have trivial destructors and
- for all non-static members of a given class, having a class (or array of classes) of its type, each such class has a trivial destructor.
Otherwise, the destructor is nontrivial.
12.4 [class.dtor] / 5 [...] A destructor is trivial if it is not user-provided and if:
- the destructor is not virtual,
- trivial destructors, and
- There are a number of types of data for each of them.
Otherwise, the destructor is non-trivial.
constexpr
expression — is its trivial destructibility.3.9 [basic.types] / 10 A type is a literal type if it is:
- [...]
- ( 9), :
- ,
- (8.5.1)
constexpr
- ,- .
3.9 [basic.types] / 10 A type is a literal type if it is:
- [...]
- a class type (Clause 9) that has all of the following properties:
- it has a trivial destructor,
- it is an aggregate type (8.5.1) or a constructor
- all of its non-volatile literal types.
variant
under these conditions it must also strive to be a literal type. Another specialization variant
should be chosen if all types in Ts
are trivially destructible. However, it is not so useful, since among the restrictions on constant expressions there is a restriction on casting a void
pointer to an object pointer:5.19 [expr.const] / 2 The conditional expressione
is the core of a constant expression only if the calculatione
following the rules of the abstract machine (1.9) is not calculated in one of the following expressions:
- [...]
- conversion from type
cv void*
to type of pointer to object;- [...]
5.19 [expr.const]/2 A conditional-expressione
is a core constant expression unless the evaluation ofe
, following the rules of the abstract machine (1.9), would evaluate one of the following expressions:
- [...]
- a conversion from type
cv void*
to a pointer-to-object type;- [...]
Source: https://habr.com/ru/post/244573/
All Articles