20

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)

einpoklum
  • 118,144
  • 57
  • 340
  • 684
  • @JohnZwinck: Would appreciate your comment/answer seeing how you wrote that answer I linked to. – einpoklum Oct 23 '16 at 09:20
  • 5
    I think JohnZwinck won't get notified unless he's interacted with this question. You might comment on his answer to the other question instead to get his attention. – DavidW Oct 23 '16 at 09:43
  • 1
    For those not aware, `boost::variant` and `boost::variant2` are two different libraries. The question is about the former, but it seems worth noting that the official documentation of `boost::variant2` states that it is API compatible with `std::variant`, with some provisos: https://www.boost.org/doc/libs/1_75_0/libs/variant2/doc/html/variant2.html#design_differences_with_stdvariant . See also the Boost project listing at https://www.boost.org/doc/libs/1_75_0/ – Max Barraclough Mar 07 '21 at 18:23
  • @MaxBarraclough: See edit, and also [this new question](https://stackoverflow.com/q/66522650/1593077). – einpoklum Mar 07 '21 at 23:42

3 Answers3

24
  • 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:

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • Is it possible to add this kind of recursiveness functionality to `std::variant` without altering the class? (But perhaps with adding stuff to `namespace std`?) – einpoklum Dec 01 '17 at 21:05
  • @einpoklum: The point of `recursive_variant` is that it allows `visit` to automatically unpack and visit the object it references. Therefore, you would have to build that machinery into `std::visit`. Which isn't possible without *replacing* `std::visit`, which you can't do. Of course, you can always make your own version of `std::visit` in your own namespace that does the unpacking/visitation. – Nicol Bolas Dec 01 '17 at 21:07
  • I wonder if it's being considered to add the `recursive_variant` to the `std` version... at least now that I'm using variants heavily, it seems like being able to make them recursive represents much of their power. – Dan Nissenbaum Dec 01 '17 at 21:38
  • 1
    @DanNissenbaum: No proposals have been made towards that end. However, [N4450 (pdf)](http://wg21.link/N4450) shows that the committee did not think particularly kindly towards it. That being said, now that it's in the standard, it is something that could be revisited. Something the committee might think better on however is a generic mechanism that allows customization of `std::visit` use. So that you could write your own `variant` that use that machinery and do your own recursive visitation. – Nicol Bolas Dec 01 '17 at 22:08
  • That's exactly my case - building an AST using Boost.Spirit. It seems so natural to have a recursive variant in that context. – Dan Nissenbaum Dec 01 '17 at 22:24
  • 2
    NicolBolas and whoever is reading this - the committe vote on recursive variants was 8 Neutral, 4 Weakly against, 2 Strongly Against. And - the question was "needed?" rather than "desirable?" or "useful?". Indeed, as Nicol suggests, with more people using `std::variant` this might change. @DanNissenbaum : you could bring this up in the standards mailing list (if not even write an actual proposal). – einpoklum Dec 01 '17 at 23:52
  • Another difference is boost currently uses std::get with a pointer instead of std::get_if for the non throwing variety. – Bruce Adams May 15 '18 at 15:15
4

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.

einpoklum
  • 118,144
  • 57
  • 340
  • 684
0

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()
sun1211
  • 1,219
  • 10
  • 15