C++17 presents std::variant and std::any, both able to store different type of values under an object. For me, they are somehow similar (are they?).
Also std::variant restricts the entry types, beside this one. Why we should prefer std::variant over std::any which is simpler to use?
The more things you check at compile time the fewer runtime bugs you have.
variant guarantees that it contains one of a list of types (plus valueless by exception). It provides a way for you to guarantee that code operating on it considers every case in the variant with std::visit; even every case for a pair of variants (or more).
any does not. With any the best you can do is "if the type isn't exactly what I ask for, some code won't run".
variant exists in automatic storage. any may use the free store; this means any has performance and noexcept(false) issues that variant does not.
Checking for which of N types is in it is O(N) for an any -- for variant it is O(1).
any is a dressed-up void*. variant is a dressed-up union.
any cannot store non-copy or non-move able types. variant can.
The type of variant is documentation for the reader of your code.
Passing a variant<Msg1, Msg2, Msg3> through an API makes the operation obvious; passing an any there means understanding the API requires reliable documentation or reading the implementation source.
Anyone who has been frustrated by statically typeless languages will understand the dangers of any.
Now this doesn't mean any is bad; it just doesn't solve the same problems as variant. As a copyable object for type erasure purposes, it can be great. Runtime dynamic typing has its place; but that place is not "everywhere" but rather "where you cannot avoid it".
The difference is that the objects are stored within the memory allocated by std::variant:
cppreference.com - std::variant
As with unions, if a variant holds a value of some object type T, the object representation of T is allocated directly within the object representation of the variant itself. Variant is not allowed to allocate additional (dynamic) memory.
and for std::any this is not possible.
As of that a std::variant, does only require one memory allocation for the std::variant itself, and it can stay on the stack.
In addition to never using additional heap memory, variant has one other advantage:
You can std::visit a variant, but not any.
Related
Boost seems to have two implementations of a variant class template:
boost::variant
boost::variant2
It is rare (though not unheard of) for Boost two include two takes on the same concept. Why has this happened for variants? How do these variants differ?
The first kind of variant class template, boost::variant, predates C++17's std::variant. See this question for its comparison to std::variant. Mainly, the difference regards what to do when an exception is thrown on construction of a value within the variant.
The std::variant choice is to allow a valueless state; the boost::variant choice is to construct the new object on the heap, not in-place, and store a pointer to that location.
boost::variant2 is a later addition, which, on the one hand, wishes to adhere to the C++17 API, but on the other hand is an expression of dissatisfaction with its choice on this matter.
boost::variant2 chooses a third option, different than both previous implementations: double-buffering. It takes up twice the size of the problematic type; constructs a new value in the unused half, and once the construction succeeds - destroys the old value in the other half. If all types are nothrow-constructible, this is not necessary and boost::variant2 will not actually have the double buffer.
This choice means that boost::variant2 can never be valueless; and indeed, its documentation title emphasizes this fact.
As far as I know each polymorphic class in C++ contains a string with a mangled type name. And RTTI is implemented by string comparison.
Is this true? Would it be more efficient to implement a centralized type storage instead?
With centralized type storage each object can just hold a pointer to type information. Dynamic casts can be implemented simply by pointer comparison.
The actual implementation is even more efficient than one pointer per object.
The Standard forbids adding any data to "standard layout" classes, so there's not even room for a pointer, let alone a string. For polymorphic classes, there will be extra metadata, but in real-world implementations, all data specific to the dynamic type of the object is stored together, and there's just one pointer needed to all of it.
As a result, because polymorphic objects already need a pointer to the virtual function dispatch table, there is zero incremental per-object cost to storing the type name. There just an extra pointer stored in the v-table alongside the function pointers, so the cost is one pointer per polymorphic type no matter how many instances exist.
Polymorphic classes contain what the compiler builder considered worthy putting in them, there is no rule or requirement to have any type information.
The concept of C++ is strongly typed, and the checking id to be done by the compiler. The compiled code is typically optimized for performance and/or size, and not to carry information that shouldn’t be needed.
Of course, some compilers offer this, but that is not the spirit of the language.
In an answer to this SO question:
What is the equivalent of boost::variant in the C++ standard library?
it is mentioned that boost::variant and std::variant differ somewhat.
What are the differences, as far as someone using these classes is concerned?
What motivation did the committee express to adopt std::variant with these differences?
What should I watch out for when coding with either of these, to maintain maximum compatibility with switching to the other one?
(the motivation is using boost::variant in pre-C++17 code)
Assignment/emplacement behavior:
boost::variant may allocate memory when performing assignment into a live variant. There are a number of rules that govern when this can happen, so whether a boost::variant will allocate memory depends on the Ts it is instantiated with.
std::variant will never dynamically allocate memory. However, as a concession to the complex rules of C++ objects, if an assignment/emplacement throws, then the variant may enter the "valueless_by_exception" state. In this state, the variant cannot be visited, nor will any of the other functions for accessing a specific member work.
You can only enter this state if assignment/emplacement throws.
Boost.Variant includes recursive_variant, which allows a variant to contain itself. They're essentially special wrappers around a pointer to a boost::variant, but they are tied into the visitation machinery.
std::variant has no such helper type.
std::variant offers more use of post-C++11 features. For example:
It forwards the noexcept status of the special member functions of its constituent types.
It has variadic template-based in-place constructors and emplacement functions.
Defect resolutions applied to C++17 may mean that it will also forward trivial copyability of its types. That is, if all of the types are trivially copyable, then so too will variant<Ts>.
It seems the main point of contention regarding the design of a variant class has been what should happen when an assignment to the variant, which should upon completion destory the old value, throws an exception:
variant<std::string, MyClassWithThrowingDefaultCtor> v = "ABC";
v = MyClassWithThrowingDefaultCtor();
The options seem to be:
Prevent this by restricting the possible representable types to nothrow-move-constructible ones.
Keep the old value - but this requires double-buffers.
Construct the new value on the heap, store a pointer to it in the variant (so the variant itself is not garbled even on exception). This is, apparently, what boost::variant does.
Have a 'disengaged' state with no value for each variant, and go to that state on such failures.
Undefined behavior
Make the variant throw when trying to read its value after something like that happens
and if I'm not mistaken, the latter is what's been accepted.
This is summarized from the ISO C++ blog post by Axel Naumann from Nov 2015.
std::variant differs slightly from the boost::variant
std::variant is declared in the header file rather than in <boost.variant.hpp>
std::variant never ever allocates memory
std::variant is usable with constexpr
Instead of writing boost::get(&variable), you have to write std::get_if(&variable) for std::variant
std::variant can not recursively hold itself and misses some other advanced techniques
std::variant can in-place construct objects
std::variant has index() instead of which()
I'm wondering what the differences are between a Boost Variant and a union data-type in c/c++. I know that a union data-type takes up the same memory location and the largest data type in the region of memory occupies the total amount of memory used e.g.
union space {
char CHAR;
float FLOAT;
int INTEGER;
}S;
should occupy 4 bytes of memory since int and float are the largest and equal size. Are there similarities and differences in other ways between Boost Variant and union data types?
I also know that a Boost Variant can take any data type and it allows data type "polymorphism" (correct me if I'm misusing a OOP topic word). Is a union data type therefore a type of polymorphism also?
The primary difference is that Boost's Variant knows which type is stored in it, so you can't make mistakes or get UB from misusing a Variant in the same way you can a union. This also permits Variant to take non-POD (i.e. actually useful) types. Variant also has a few extra tricks like permitting visitors and recursive variants.
The best guide to using unions is "Don't, because it's almost impossible to put them to good use without invoking UB". This does not apply to Variant, so it's a lot safer to recommend.
Boost variant emulates a union but it does not use a union in its implementation. Instead it uses aligned storage and placement new.
It is polymorphic in the sense that if you apply a visitor object on a variant then it will pick the right overload for you. This selection must happen at runtime, but the object code for this is unrolled at compile time. So it's quite fast.
I am trying to write a quadtree sparse matrix class. In short, a quadtree_matrix<T> is either the zero matrix or a quadruple (ne, nw, se, sw) of quadtree_matrix<T>.
I'd like eventually to test different allocation schemes since this will probably impact the performance of linear algebra operations. So I will also template quadtree_matrix on a standard allocator type, so that I can reuse existing allocators.
I will have to allocate two different kind of data: either a T, or a node, which contains four pointers (to either T or node). For all the algorithms I will consider, I know for sure what kind of data to expect because I know what are the sizes of the submatrices I am facing at any point of the algorithm (I don't even need to store these sizes).
I will of course be using two different allocators: this is ok, since allocator types provide the rebind template and a template copy constructor (and are intended to be used as value types, as the get_allocator members of standard containers suggest by returning a copy).
The problem is that allocator member functions use a certain pointer type, which is not required to be a vanilla pointer. Some allocators (boost interprocess allocators) use this feature extensively.
If the allocator pointer types were garden variety pointers, I would have no problems: at the very least, I could use pointers to void and reinterpret_cast them to the right type (either node* or T*). I could also use a union (probably better).
As far as I know, there is no requirement on the PODness of the allocator::pointer types. They are only required to be random access iterators.
Now, my question is:
Given an allocator class template A<T> (or its equivalent A::rebind<T>::other), is there any guarantee on:
The ability to static cast A<T>::pointer to A<U>::pointer provided U is an accessible base of T ?
The ability to static cast A<T>::pointer to A<U>::pointer provided T is an accessible base of U and the "runtime type" (whatever this means in this context) of the castee is U ?
The type A<void>::pointer (if this makes sense) ?
Or is there a solution to my problem I didn't think about ?
From the tables in 20.1.5/2 it clearly indicates that the type of A<T>::pointer must be "pointer to T". Since those pointer types are normally convertible your 1 and 2 are true. It follows then that A<void>::pointer must be void*.
EDIT:
There's also explicit wording in 20.1.5/4 (it applies to what standard containers may assume about allocators):
The typedef members pointer,
const_pointer, size_type, and
difference_type are required to be
T*,T const*, size_t, and ptrdiff_t,
respectively.
No, not really.
There is a requirement that A<T>::pointer is convertible to A<T>::const_pointer and A<T>::void_pointer but that is about all I can find.
A<void>::pointer is likely to be void*, unless you have some fancy special memory.
Note that even if a union is usable, I would still not use one, especially because here you could probably benefit from some form of automatic memory management (in order for your contain not to leak), which requires fancy classes.
I would therefore recommend a two-steps approach:
Write a small smart pointer that use a given allocator to perform the destruction (instead of delete)
Use boost::variant on your pointers
This way you have both automatic memory management and compacity.