I have a class wrapping a boost variant that only contains memmovable types (QList, QString, int etc).
May I declare that wrapper class memmovable to Qt containers?
A boost::variant contains only an integral index and an aligned_storage, which is guaranteed by the standard to be a POD. It has no virtual members, but has user-defined constructors and a destructor. As a consequence, boost::variant is not a POD and trying to memmove it is UB (well, I think it is UB, I don't find a definitive reference in the standard).
However, the same can be said for QList, QString, etc. Apparently, Qt assumes that some non-POD types can be safely memmoved, and makes a distinction between POD (so-called "primitive types") and "movable types".
Consequently, if you think it is safe to memmove a QList, you can consider it safe to memmove a boost::variant containing memmovable types.
You probably know that memmoving non-POD types is technically undefined behaviour. That aside, variant doesn't contain anything that would be problematic if memmoved. Since you mention QList and QString as being memmovable, and I have difficulty believing that they are PODs (although I haven't seen them), boost::variant is no worse.
Related
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.
Please explain what is the difference between union and std::variant and why std::variant was introduced into the standard? In what situations should we use std::variant over the old-school union?
Generally speaking, you should prefer variant unless one of the following comes up:
You're cheating. You're doing type-punning or other things that are UB but you're hoping your compiler won't break your code.
You're doing some of the pseudo-punnery that C++ unions are allowed to do: conversion between layout-compatible types or between common initial sequences.
You explicitly need layout compatibility. variant<Ts> are not required to have any particular layout; unions of standard layout types are standard layout.
You need low-level support for in-place switching of objects. Using a memory buffer for such things doesn't provide the trivial copying guarantees that you could get out of a union.
The basic difference between the two is that variant knows which type it stores, while union expects you to keep track of that externally. So if you try to access the wrong item in a variant, you get an exception or nullptr. By contrast, doing so with a union is merely undefined behavior.
union is a lower-level tool, and thus should only be used when you absolutely need that lower-level.
variant also has machinery for doing visitation, which means you get to avoid having a bunch of if statements where you ask "if it is type X, do this. If it is type Y, do that, etc".
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 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.
Suddenly in this article ("problem 2") I see a statement that C++ Standard prohibits using STL containers for storing elemants of class if that class has an overloaded operator&().
Having overloaded operator&() can indeed be problematic, but looks like a default "address-of" operator can be used easily through a set of dirty-looking casts that are used in boost::addressof() and are believed to be portable and standard-compilant.
Why is having an overloaded operator&() prohibited for classes stored in STL containers while the boost::addressof() workaround exists?
Without having looked at the links, I suppose the tricks in boost::addressof() were invented well after the requirement to not to overload unary prefix & for objects to be held in containers of the std lib.
I vaguely remember Pete Becker (then working for Dinkumware on their standard library implementation) once stating that everyone who overloads the address-of operator and expects their standard library implementation still to work should be punished by having to implement a standard library which does this.
Probably because it's less hassle to just prohibit the use of overloaded operator&() classes than to create a std::addressof() function and replace every use of & in container code with it.
The standard was finalized in 1998 with fixes in 2003, whereas boost::addressof dates to early 2002.
Moreover, it's not clear that addressof is the answer. Overloads of operator&() indicate that raw pointers are supposed to be avoided. The Allocator::address member provides the best interface to get from Allocator::reference to Allocator::pointer, so in general theory, you should be able to effectively introduce an operator& override to an otherwise well-behaved class with a custom allocator.
Considering that references do almost everything that pointers do, and the Allocator interface abstracts everything else, there should be no need for raw pointers.
Convenience to the library implementers should not be an issue. The ill-defined semantics of Allocator::pointer are a problem, and what I've read so far in C++0x doesn't clear that up.
C++0x removes any mention of operator& from CopyConstructible, and furthermore doesn't require anything-Constructible for container arguments at all — the user can stick to emplace. Even vector only requires Destructible, although I suppose actually using insert or erase would require more.
(Note that, in the strictest reading, overloads are not forbidden in C++03. You are just not allowed to change the value or type of the builtin.)