What are the differences between std::variant and boost::variant?
Assignment/emplacement behavior:
boost::variant
may allocate memory when performing assignment into a livevariant
. There are a number of rules that govern when this can happen, so whether aboost::variant
will allocate memory depends on theTs
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 thevariant
may enter the "valueless_by_exception" state. In this state, thevariant
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 avariant
to contain itself. They're essentially special wrappers around a pointer to aboost::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>
.
Difference between std::optional and boost::optional when its value is a variant
boost::optional
doesn't have a constructor that takes arguments to pass to the underlying type but std::optional
does (see constructor number 8)
You need to explicitly call the optional constructor:
boost::optional<boost::variant<int, std::string>> f()
{
return boost::optional<boost::variant<int, std::string>>{5};
}
Or as the construction from the contained value type is non-explicit:
boost::optional<boost::variant<int, std::string>> f()
{
return boost::variant<int, std::string>{5};
}
How to properly replace boost::variant by std::variant?
As you have duplicate types in your variant some of the constructors are disabled:
This overload only participates in overload resolution if there is exactly one occurrence of T in Types...
You need to use the constructor with an explicit type index:
return std::variant<T...>(std::in_place_index<n>, std::get<n>(tpl));
Your original boost code has undefined behaviour:
Each type specified as a template argument to variant must be distinct after removal of qualifiers. Thus, for instance, both
variant<int, int>
andvariant<int, const int>
have undefined behavior.
The standard libary implementation does support duplicate types and therefore prevents you from accidentally constructing an ambigous variant. For example what should the following do:
variant<std::string, double, double, int> t = 4.5;
With boost it is UB, either of the double
values might be initialised or it might do something completely different. The standard library explitly makes this a compiler error so that you have to choose which of your double
s you want to initialise.
Differences between `boost::any` and `std::any`
I'm interested to the differences between the behavior and the guarantees.
There aren't any behavioral differences; not really. They both have the same requirements on the ValueType (copy-constructible, and a destructor that doesn't emit exceptions). They both provide the same operations on the values they store, with pretty much identical exception guarantees.
The principle difference is that boost::any
's implementation at present doesn't implement small object optimization, while std::any
implementations may provide it.
Boost.Any vs. Boost.Variant
Have you looked at the comparison in the variant library already?
(Not sure what states from external sources are, so it's kind of hard to say what's more appropriate for you.)
Transitioning Boost Spirit parser from boost::variant to std::variant
Somehow
boost::variant
avoids the error.
Yeah. Boost variant has attribute propagation
support.
Besides, boost::variant
has special handling of boost::recursive_wrapper
so it might be a double no-fly.
A good article about recursive
std::variant
s is here https://vittorioromeo.info/index/blog/variants_lambdas_part_2.html
What's wrong with boost::variant
?
If you want you can write some transformation traits, or even look into x3::variant - it might suit you better?
Live On Coliru
#include <string>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
#include <boost/variant/recursive_wrapper.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
namespace x3 = boost::spirit::x3;
namespace ascii = boost::spirit::x3::ascii;
struct Recurse;
using Base = x3::variant<
std::string,
x3::forward_ast<Recurse> >;
struct Recurse
{
int _i;
Base _base;
};
BOOST_FUSION_ADAPT_STRUCT(
Recurse,
(int, _i),
(Base, _base)
)
const x3::rule<class Base_, Base> base = "base";
const auto operand = *x3::char_("a-zA-Z0-9_") | base;
const auto base_def = (x3::int_ >> operand) | operand;
BOOST_SPIRIT_DEFINE(base)
int main()
{
std::string text;
Base result;
x3::phrase_parse(std::begin(text), std::end(text), base, ascii::space, result);
return 0;
}
Side note: No
x3::forward_ast<>
does not help withstd::variant
, confirming thatstd::variant
just lacks support in x3
UPDATE
You can work-around things by making your Base
a derived struct with the required machinery to indicate to Spirit that it is a variant (and over which types). That way you don't have to go through trait specialization hell:
struct Recurse;
struct Base : std::variant<std::string, boost::recursive_wrapper<Recurse> > {
using BaseV = std::variant<std::string, boost::recursive_wrapper<Recurse> >;
using BaseV::BaseV;
using BaseV::operator=;
struct adapted_variant_tag {};
using types = boost::mpl::list<std::string, Recurse>;
};
struct Recurse {
int _i;
Base _base;
};
As you can see, it's basically the same¹, but adds adapted_variant_tag
and types
nested types.
Note that by cleverly hardcoding the types
sequence, we can pretend to handle the recursive wrapper smartly. We're lucky that this is enough to fool the system.
Adding some debug output and test-cases:
Live On Coliru
#include <string>
#include <variant>
#include <iostream>
#include <iomanip>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
#include <boost/variant/recursive_wrapper.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
namespace x3 = boost::spirit::x3;
namespace ascii = boost::spirit::x3::ascii;
namespace { // for debug
template<class T>
std::ostream& operator<<(std::ostream& os, boost::recursive_wrapper<T> const& rw) {
return os << rw.get();
}
template<class... Ts>
std::ostream& operator<<(std::ostream& os, std::variant<Ts...> const& sv) {
std::visit([&os](const auto& v) { os << v; }, sv);
return os;
}
}
struct Recurse;
struct Base : std::variant<std::string, boost::recursive_wrapper<Recurse> > {
using BaseV = std::variant<std::string, boost::recursive_wrapper<Recurse> >;
using BaseV::BaseV;
using BaseV::operator=;
struct adapted_variant_tag {};
using types = boost::mpl::list<std::string, Recurse>;
};
struct Recurse {
int _i;
Base _base;
friend std::ostream& operator<<(std::ostream& os, Recurse const& r) {
return os << "[" << r._i << ", " << r._base << "]";
}
};
BOOST_FUSION_ADAPT_STRUCT(
Recurse,
(int, _i),
(Base, _base)
)
static_assert(x3::traits::is_variant<Base>::value);
const x3::rule<class Base_, Base> base = "base";
const auto operand = *x3::char_("a-zA-Z0-9_") | base;
const auto base_def = (x3::int_ >> operand) | operand;
BOOST_SPIRIT_DEFINE(base)
int main()
{
for (std::string const text : { "yeah8", "32 more" }) {
Base result;
auto f = begin(text), l = end(text);
if (x3::phrase_parse(f, l, base, ascii::space, result)) {
std::cout << "Result: " << result << "\n";
} else {
std::cout << "Failed\n";
}
if (f!=l) {
std::cout << "Remaining input: " << std::quoted(std::string(f,l)) << "\n";
}
}
}
Which prints
Result: yeah8
Result: [32, more]
Update 2: Icing The Cake
Here's the traits required to make std::variant
just work:
namespace boost::spirit::x3::traits {
template<typename... t>
struct is_variant<std::variant<t...> >
: mpl::true_ {};
template <typename attribute, typename... t>
struct variant_has_substitute_impl<std::variant<t...>, attribute>
{
typedef std::variant<t...> variant_type;
typedef typename mpl::transform<
mpl::list<t...>
, unwrap_recursive<mpl::_1>
>::type types;
typedef typename mpl::end<types>::type end;
typedef typename mpl::find<types, attribute>::type iter_1;
typedef typename
mpl::eval_if<
is_same<iter_1, end>,
mpl::find_if<types, traits::is_substitute<mpl::_1, attribute>>,
mpl::identity<iter_1>
>::type
iter;
typedef mpl::not_<is_same<iter, end>> type;
};
template <typename attribute, typename... t>
struct variant_find_substitute<std::variant<t...>, attribute>
{
typedef std::variant<t...> variant_type;
typedef typename mpl::transform<
mpl::list<t...>
, unwrap_recursive<mpl::_1>
>::type types;
typedef typename mpl::end<types>::type end;
typedef typename mpl::find<types, attribute>::type iter_1;
typedef typename
mpl::eval_if<
is_same<iter_1, end>,
mpl::find_if<types, traits::is_substitute<mpl::_1, attribute> >,
mpl::identity<iter_1>
>::type
iter;
typedef typename
mpl::eval_if<
is_same<iter, end>,
mpl::identity<attribute>,
mpl::deref<iter>
>::type
type;
};
template <typename... t>
struct variant_find_substitute<std::variant<t...>, std::variant<t...> >
: mpl::identity<std::variant<t...> > {};
}
That's a lot of noise but you can put it away in a header somewhere.
BONUS
Fixing the grammar:
- you probably meant to have
lexeme[]
around the string production - you probably meant to have a minimal lenght of the string (+char_, not *char_) seeing that there are no delimiters
- you may have have to reorder the branches because the string production would gobble up integers for recursed rules.
Here's my touched-up take on the grammar, where the rules closely mirror the AST, as usually makes sense:
namespace Parser {
static_assert(x3::traits::is_variant<Base>::value);
const x3::rule<class Base_, Base> base = "base";
const auto string = x3::lexeme[+x3::char_("a-zA-Z0-9_")];
const auto recurse = x3::int_ >> base;
const auto base_def = recurse | string;
BOOST_SPIRIT_DEFINE(base)
}
Simplify Fusion
Last but not least, in C++11 era you can deduce the adapted fusion members:
BOOST_FUSION_ADAPT_STRUCT(Recurse, _i, _base)
Live Full Demo
Live On Coliru
#include <string>
#include <variant>
#include <iostream>
#include <iomanip>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
#include <boost/variant/recursive_wrapper.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
namespace x3 = boost::spirit::x3;
namespace ascii = boost::spirit::x3::ascii;
namespace { // for debug
template<class T>
std::ostream& operator<<(std::ostream& os, boost::recursive_wrapper<T> const& rw) {
return os << rw.get();
}
template<class... Ts>
std::ostream& operator<<(std::ostream& os, std::variant<Ts...> const& sv) {
std::visit([&os](const auto& v) { os << v; }, sv);
return os;
}
}
struct Recurse;
using Base = std::variant<
std::string,
boost::recursive_wrapper<Recurse> >;
namespace boost::spirit::x3::traits {
template<typename... T>
struct is_variant<std::variant<T...> >
: mpl::true_ {};
template <typename Attribute, typename... T>
struct variant_has_substitute_impl<std::variant<T...>, Attribute>
{
typedef std::variant<T...> variant_type;
typedef typename mpl::transform<
mpl::list<T...>
, unwrap_recursive<mpl::_1>
>::type types;
typedef typename mpl::end<types>::type end;
typedef typename mpl::find<types, Attribute>::type iter_1;
typedef typename
mpl::eval_if<
is_same<iter_1, end>,
mpl::find_if<types, traits::is_substitute<mpl::_1, Attribute>>,
mpl::identity<iter_1>
>::type
iter;
typedef mpl::not_<is_same<iter, end>> type;
};
template <typename Attribute, typename... T>
struct variant_find_substitute<std::variant<T...>, Attribute>
{
typedef std::variant<T...> variant_type;
typedef typename mpl::transform<
mpl::list<T...>
, unwrap_recursive<mpl::_1>
>::type types;
typedef typename mpl::end<types>::type end;
typedef typename mpl::find<types, Attribute>::type iter_1;
typedef typename
mpl::eval_if<
is_same<iter_1, end>,
mpl::find_if<types, traits::is_substitute<mpl::_1, Attribute> >,
mpl::identity<iter_1>
>::type
iter;
typedef typename
mpl::eval_if<
is_same<iter, end>,
mpl::identity<Attribute>,
mpl::deref<iter>
>::type
type;
};
template <typename... T>
struct variant_find_substitute<std::variant<T...>, std::variant<T...> >
: mpl::identity<std::variant<T...> > {};
}
static_assert(x3::traits::is_variant<Base>{}, "");
struct Recurse
{
int _i;
Base _base;
friend std::ostream& operator<<(std::ostream& os, Recurse const& r) {
return os << "[" << r._i << ", " << r._base << "]";
}
};
BOOST_FUSION_ADAPT_STRUCT(Recurse, _i, _base)
namespace Parser {
static_assert(x3::traits::is_variant<Base>::value);
const x3::rule<class Base_, Base> base = "base";
const auto string = x3::lexeme[+x3::char_("a-zA-Z0-9_")];
const auto recurse = x3::int_ >> base;
const auto base_def = recurse | string;
BOOST_SPIRIT_DEFINE(base)
}
int main()
{
for (std::string const text : { "yeah8", "32 more", "18 766 most" }) {
Base result;
auto f = begin(text), l = end(text);
if (x3::phrase_parse(f, l, Parser::base, ascii::space, result)) {
std::cout << "Result: " << result << "\n";
} else {
std::cout << "Failed\n";
}
if (f!=l) {
std::cout << "Remaining input: " << std::quoted(std::string(f,l)) << "\n";
}
}
}
Which prints:
Result: yeah8
Result: [32, more]
Result: [18, [766, most]]
¹ (the subtle difference MAY bite you in generic programming where you need to access the base-class explicitly)
Error while trying to use the boost::variant - No matching function for call
Here's my imagined self-contained tester:
Live On Coliru
#include <boost/variant.hpp>
#include <iostream>
struct X;
struct Y;
template <typename... T> struct ClassA {
void operate(double d, int a, float b) const
{
std::cout << __PRETTY_FUNCTION__ << "(" << d << "," << a << "," << b << ")\n";
}
};
struct Output {
int a;
float b;
};
typedef boost::variant<ClassA<X, Y>, ClassA<>> ClassAGeneric;
class Operation // : public boost::static_visitor<Output>
{
public:
double d;
int a;
float b;
Output operator()(ClassA<X, Y> const& obj) const
{
obj.operate(d, a, b);
return Output{a, b};
}
Output operator()(ClassA<> const& obj) const
{
obj.operate(d, a, b);
return Output{a, b};
}
};
int main() {
Operation op {3.14, 42, 9e-2f};
ClassAGeneric v1 = ClassA<X,Y>{};
ClassAGeneric v2 = ClassA<>{};
apply_visitor(op, v1);
apply_visitor(op, v2);
}
Prints
void ClassA::operate(double, int, float) const with T = {X, Y}
void ClassA::operate(double, int, float) const with T = {}
Unsurprisingly that works. Now, one pitfall could be when you failed to make the operate
member function const
and the arguments are, in fact, const
.
Also note that you can greatly simplify the visitor (especially assuming C++14): Live On Coliru
std::variant and ambiguous initialization
Before P0608, variant<int, long double> v{42.3}
also has the ambiguous issue since 42.3
can be converted to int
or long double
.
P0608 changed the behavior of variant
's constructors:
template<class T> constexpr variant(T&& t) noexcept(see below);
- Let
Tj
be a type that is determined as follows: build an imaginary functionFUN(Ti)
for each alternative typeTi
for whichTi x[] = {std::forward<T>(t)};
is well-formed for some invented variablex
and,
ifTi
iscv bool
,remove_cvref_t<T>
is bool. The overloadFUN(Ti)
selected by overload resolution for the expressionFUN(std::forward<T>(t))
defines the alternativeTj
which is the type
of the contained value after construction.
In your example, the variant
has two alternative types: int
and long double
, so we can build the following expression
int x[] = {std::forward<double>(42.3)}; // #1
long double y[] = {std::forward<double>(42.3)}; // #2
Since only #2
is well-formed, the variant
successfully deduces the type of the contained value type long double
.
Related Topics
Advantage of Using Trailing Return Type in C++11 Functions
C++ Virtual Function Table Memory Cost
How to See the Output of the Visual C++ Preprocessor
Std::Shared_Ptr and Initializer Lists
Observable Behavior and Undefined Behavior -- What Happens If I Don't Call a Destructor
How Does the Friend Keyword (Class/Function) Break Encapsulation in C++
Load Resource as Byte Array Programmaticaly in C++
How to Find If a Variable Is Allocated in Stack or Heap
Convert Bitmap to Png In-Memory in C++ (Win32)
What Does Afx_Manage_State(Afxgetstaticmodulestate()) Do Exactly
Why Can't I Use <Experimental/Filesystem> with G++ 4.9.2
What Are the Reasons That Extending the Std Namespace Is Considered Undefined Behavior
Why How to Call a Non-Constexpr Function Inside a Constexpr Function
Should I Include Stddef.H or Cstddef for Size_T