Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What are the differences between std::variant and boost::variant?

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)

like image 968
einpoklum Avatar asked Oct 23 '16 09:10

einpoklum


People also ask

What is boost :: variant?

Boost. Variant, part of collection of the Boost C++ Libraries. It is a safe, generic, stack-based discriminated union container, offering a simple solution for manipulating an object from a heterogeneous set of types in a uniform manner.

What does std :: variant do?

The class template std::variant represents a type-safe union. An instance of std::variant at any given time either holds a value of one of its alternative types, or in the case of error - no value (this state is hard to achieve, see valueless_by_exception).

What is boost :: Apply_visitor?

boost::apply_visitor — Allows compile-time checked type-safe application of the given visitor to the content of the given variant, ensuring that all types are handled by the visitor.

What is STD Monostate?

std::monostate is a class that has exactly one value. It is default constructable and supports all the comparison operations. std::monostate is about as simple of a type as one could concoct. These properties turn out to be useful for writing template code. The first use case is in testing.


2 Answers

  • 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>.

like image 146
Nicol Bolas Avatar answered Oct 19 '22 08:10

Nicol Bolas


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.

like image 20
einpoklum Avatar answered Oct 19 '22 08:10

einpoklum