I am trying to define and visit a "recursive" boost::variant
using an incomplete wrapper class and std::vector
as my indirection techniques. My implementation works with libstdc++, but not with libc++.
This is the way I am defining my variant:
struct my_variant_wrapper;
using my_variant_array = std::vector<my_variant_wrapper>; // <- indirection here
using my_variant = boost::variant<int, my_variant_array>;
struct my_variant_wrapper
{
my_variant _v;
template <typename... Ts>
my_variant_wrapper(Ts&&... xs) : _v(std::forward<Ts>(xs)...) { }
};
I am using std::vector
to introduce indirection (so that dynamic allocation will prevent my_variant
to have infinite size).
I am quite confident I am allowed to use std::vector<my_variant_wrapper>
, where my_variant_wrapper
is an incomplete type, because of paper N4510 ("Minimal incomplete type support for standard containers"):
The paper was approved, according to WG21's 2015 page.
The features has always been supported in libstdc++, according to this page.
It was implemented in libc++ 3.6, according to this page.
I am then visiting the variant as follows:
struct my_visitor
{
void operator()(int x) const { }
void operator()(const my_variant_array& arr) const
{
for(const auto& x : arr)
boost::apply_visitor(*this, x._v);
}
};
int main()
{
my_variant v0 = my_variant_array{
my_variant{1}, my_variant{2}, my_variant_array{
my_variant{3}, my_variant{4}
}
};
boost::apply_visitor(my_visitor{}, v0);
}
A minimal complete example is available on coliru.
I'm using the following flags:
-std=c++1z -Wall -Wextra -Wpedantic
BOOST_VERSION
evaluates to106100
.
The code:
Compiles and runs as intended on:
g++ (tested versions: 6.1 and 7), with libstdc++.
clang++ (tested versions: 3.8), with libstdc++.
(as a bonus, it also works with
std::variant
by making the appropriate changes!)
Fails to compile on:
- clang++ (tested versions: 3.8, 4), with libc++.
This is the error I get while compiling on clang++ with libc++:
In file included from prog.cc:2:
In file included from /usr/local/boost-1.61.0/include/boost/variant.hpp:17:
/usr/local/boost-1.61.0/include/boost/variant/variant.hpp:1537:28: error: no matching member function for call to 'initialize'
initializer::initialize(
~~~~~~~~~~~~~^~~~~~~~~~
/usr/local/boost-1.61.0/include/boost/variant/variant.hpp:1692:9: note: in instantiation of function template specialization 'boost::variant<int, std::__1::vector<my_variant_wrapper, std::__1::allocator<my_variant_wrapper> > >::convert_construct<my_variant_wrapper>' requested here
convert_construct(operand, 1L);
^
prog.cc:15:38: note: in instantiation of function template specialization 'boost::variant<int, std::__1::vector<my_variant_wrapper, std::__1::allocator<my_variant_wrapper> > >::variant<my_variant_wrapper>' requested here
my_variant_wrapper(Ts&&... xs) : _v(std::forward<Ts>(xs)...) { }
^
/usr/local/libcxx-head/include/c++/v1/memory:1783:31: note: in instantiation of function template specialization 'my_variant_wrapper::my_variant_wrapper<my_variant_wrapper &>' requested here
::new((void*)__p) _Up(_VSTD::forward<_Args>(__args)...);
^
/usr/local/libcxx-head/include/c++/v1/memory:1694:18: note: in instantiation of function template specialization 'std::__1::allocator<my_variant_wrapper>::construct<my_variant_wrapper, my_variant_wrapper &>' requested here
{__a.construct(__p, _VSTD::forward<_Args>(__args)...);}
^
...
The full error is available on wandbox.
Why is the code not compiling with libc++? (Could this be a defect in libc++'s N4510 implementation that needs to be reported?)
The error seems to suggest that the variant fails to detect what members should be initialized, but I honestly couldn't make much sense of it. I am also confused by the fact that using libstdc++ (with the same boost version) works as expected.