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.
Related
(This question stems for this more specific questions about stream iterators.)
A type is said [Stepanov, McJones] to be Regular if:
it is equality-comparable
it is assignable (from other values of the type)
it is destructible
it is default-constructible (i.e. constructible with no arguments)
It has a (default) total ordering of its values
(and there's some wording about "underlying type" which I didn't quite get.)
Some/many people claim that, when designing types - e.g. for the standard library of C++ - it is worthwhile or even important to make an effort to make these types regular, possibly ignoring the total-order requirement. Some mention the maxim:
Do as the ints do.
and indeed, the int type satisfies all these requirements. However, default-constructible types, when constructed, hold some kind of null, invalid or junk value - ints do. A different approach is requiring initialization on construction (and de-initialization on destruction), so that the object's lifetime corresponds to its time of viability.
To contrast these two approaches, one can perhaps think about a T-pointer type, and a T-reference or T-reference-wrapper type. The pointer is basically regular (the ordering assumes a linear address space), and has nullptr which one needs to look out for; the reference is, under the hood, a pointer - but you can't just construct a reference-to-nothing or a junk-reference. Now, pointers may have their place, but we mostly like to work with references (or possibly reference wrappers which are assignable).
So, why should we prefer designing (as library authors) and using regular types? Or at least, why should we prefer them in so many contexts?
I doubt this is the most significant answer, but you could make the argument that "no uninitialized state" / "no junk value" is a principle that doesn't sit well with move-assignment: After you move from your value, and take its resources away - what state does it remain in? It is not very far from default-constructing it (unless the move-assignment is based on some sort of swapping; but then - you could look at move construction).
The rebuttal would be: "Fine, so let's not have such types move-assignable, only move-destructible"; unfortunately - C++ decided sometime in the 2000's to go with non-destructive moves. See also this question & answer by #HowardHinnant:
Why does C++ move semantics leave the source constructed?
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.
For the case when std::promise<> is instantiated with a non reference type, why does the set_value() method have two distinct overloads as opposed to one pass by value overload?
so instead of the following two
std::promise::set_value(const Type& value);
std::promise::set_value(Type&& value);
just one
std::promise::set_value(Type value);
This has at least the following two benefits
Enable users to move the value into the promise/future when they want, since the API argument is a value type. When copying is not supported it is obvious that the value is going to be copied. Further when the expression being passed into the function is a prvalue it can be completely elided easily by the compiler (especially so in C++17)
It conveys the point that the class requires a copy of the value a lot better and succinctly than two overloads which accomplish the same task.
I was making a similar API (as far as ownership is concerned) and I was wondering what benefits the design decision employed by the C++ standard library has as opposed to what I mentioned.
Thanks!
Passing an argument by value if it needs to be "transferred" unconditionally and then moving from it is a neat little trick, but it does incur at least one mandatory move. Therefore, this trick is best in leaf code that is used rarely or only in situations that are completely under the control of the author.
By contrast, a core library whose users and uses are mostly unknown to the author should not unnecessarily add avoidable costs, and providing two separate reference parameter overloads is more efficient.
In a nutshell, the more leaf and user you are, the more you should favour simplicity over micro-optimizations, and the more library you are, the more you should go out of your way to be general and efficient.
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()
Lately I see a lot of material about generic programming, and I still cannot wrap my head around one thing, when designing types. I am not sure what is the best way, let me explain.
For some types, it is natural to provide a default constructor.
All the possible constructions of that type will be valid or a default makes sense, so it makes sense to provide a default. This is the case for basic types.
Later, there are some types for which default constructing them does not yield a value. For example, in the standard library we have std::function<Sig> and std::thread, for example. Nevertheless, they are default-constructible, even if they are not holding a value.
Later, we have the proposed optional<T> in the standard. It makes a lot of sense to use it for basic types, since for basic types all the possible assignments represent a valid value (except double and float NaN), but I don't see how you would use it for a thread or a std::function<Sig>, since these types don't hold a "value" when constructed. It is AS IF these types had "optional" embedded in the type directly.
This has these drawbacks. Since there is no "natural" default (or value) construction, such as with an int:
Now I have to litter my class with if (valid) in all my design and signal the error. OR
make it less safe to use if I don't do this check. Precondition -> assign before using if default-constructed.
So when I want to design a type, I always find the question: should I make it default constructible?
Pros:
Easier to reuse in more generic contexts, because my type will more easily model SemiRegular or Regular if I add the appropiate operations.
Cons:
Litter the whole class with if statements or making a contract with the user in which the class is more unsafe to use.
For example, let's say I have a class Song with ID, artist, title, duration and year. It is very nice for the standard library to make the type default constructible. But:
I can't just find a natural way to construct a "default Song".
I have to litter with if (validsong) or make it unsafe to use.
So my questions are:
How should I design a type that has no "natural (as in value)" defaults? Should I provide a default constructor or not?
In the case I choose to provide a default constructor, how does optional<T> fit into all this puzzle? My view is that making a type that is not "naturally" default constructible provide a default constructor makes optional<T> useless in this case.
Should optional<T> just be used for types whose domain of values is complete, meaning, I cannot assign an invalid value to its representation because all of them hold a value, such as in int?
Why were types such as std::function<Sig> made default constructible in the first place in the standard? When constructed, it does not hold a value, so I don't see why a default constructor should be provided. You could always do: optional<function<void ()>>, for example. Is this just a design choice and both are valid or there is one design, in this case, about choosing default vs non-default constructible superior to the other?
(Note: a problem with lots of questions in one question is that some parts of it can be duplicate. Best to ask smaller questions, and check each for prior posts. "One question per question" is a good policy; easier said than done sometimes, I guess.)
Why were types such as std::function made default constructible in the first place in the standard? When constructed, it does not hold a value, so I don't see why a default constructor should be provided. You could always do: optional<function<void ()>>, for example.
See Why do std::function instances have a default constructor?
How should I design a type that has no "natural (as in value)" defaults? Should I provide a default constructor or not?
Default constructors for types that have a tough time meaningfully defining themselves without some kind of data is how a lot of classes implement a null value. Is optional a better choice? I usually think so, but I'm assuming you're aware that std::optional was voted out of C++14. Even if it were the perfect answer it can't be everyone's answer...it's not soup yet.
It will always add some overhead to do runtime tracking of if the value is bound or not. Perhaps not a lot of overhead. But when you are using a language whose raison d'etre is to allow abstraction while still letting you shoot yourself in the foot as close to the metal as you want...shaving off a byte per value in a giant vector can be important.
So even if optional<T> semantics and compile-time checking were perfect, you still might face a scenario where it's advantageous to scrap it and allow your type to encode its own nullity. Gotta push those pixels or polygons or packets or... pfafftowns.
In the case I choose to provide a default constructor, how does optional fit into all this puzzle? My view is that making a type that is not "naturally" default constructible provide a default constructor makes optional useless in this case.
Should optional just be used for types whose domain of values is complete, meaning, I cannot assign an invalid value to its representation because all of them hold a value (except float and double NaN I guess).
In my own case, I found myself wanting to distinguish at compile-time checking between routines that could handle null pointers and those which could not. But suddenly an optional<pointer> offered this situation of either the optional being unbound, being bound to a null pointer, and being bound to a non-null pointer. The compile-time sanity check seeming less the win it had.
So how about optional references? They're controversial to the point that last I heard they're one of the sticking points in the set of things that delayed std::optional from C++14. Which was a bit annoying after I'd converted my optional pointers to optional references. :-/
I had a vague idea to write a book about "pathological C++" where you pick some idea and start taking it to its logical conclusions. optional<T> was one kick I got on and going with essentially the principles you identify. Remove the possibility of "nullity" from being encoded in the type itself, and then suddenly you can get the compiler doing the type-checking for whether a given bit of code is prepared to expect a null or not.
(These days I tend toward suspecting if you get very hung up on this kind of "pathological C++" you'll wind up reinventing Haskell. :-/ See the popular Data.Maybe monad.)