Use ‘STD:: vector’ containing incomplete types to recursively define and access’ boost:: variant ‘– libstdc vs libc

I tried to use the incomplete wrapper class and STD:: vector as my indirect technique to define and access the "recursive" boost:: variant My implementation works for libstdc, but not for libc

This is how I define my variants:

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 use STD:: vector to introduce indirection (so dynamic allocation will prevent my_variant from having unlimited size)

I am confident that I can use STD:: vector < My_ variant_ Wrapper >, where my_ variant_ Wrapper is incomplete type because paper n4510 ("minimum incomplete type support for standard containers"):

>The paper was approved according to wg21's 2015 page. > According to this page, libstdc always supports these functions. > According to this page, it is implemented in libc 3.6

I then visited 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 use the following flags:

> BOOST_ The calculation result of version is 106100

code:

>Compile and run as expected:

>G (beta version: 6.1 and 7), libstdc. > Clang (beta version: 3.8), libstdc. > (as a reward, it also applies to STD:: variant to make appropriate changes!)

>Unable to compile:

>Clang (beta version: 3.8,4), with libc

This is the error I got when compiling 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::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 isn't the code compiled with libc? (could this be a defect that needs to be reported in the n4510 implementation of libc?)

The error seems to indicate that the variant failed to detect which members should be initialized, but in fact I can't understand it I'm also confused by the fact that using libstdc (with the same boost version) works as expected

Solution

I saw this in my backtracking:

This clearly indicates that your constructor template is Hijacking the copy constructor

Limit it and your problem disappears

The difference between implementations is due to the way the vector's copy constructor copies elements Libstdc treats the source element as const:

vector(const vector& __x)
  : _Base(__x.size(),_Alloc_traits::_S_select_on_copy(__x._M_get_Tp_allocator()))
{
    this->_M_impl._M_finish =
    std::__uninitialized_copy_a(__x.begin(),__x.end(),this->_M_impl._M_start,_M_get_Tp_allocator());
}

Because begin () and end () are called in const vector x. They return constant iterators

Libc treats the source element as non const:

template <class _Tp,class _Allocator>
vector<_Tp,_Allocator>::vector(const vector& __x)
    : __base(__alloc_traits::select_on_container_copy_construction(__x.__alloc()))
{
#if _LIBCPP_DEBUG_LEVEL >= 2
    __get_db()->__insert_c(this);
#endif
    size_type __n = __x.size();
    if (__n > 0)
    {
        allocate(__n);
        __construct_at_end(__x.__begin_,__x.__end_,__n);
    }
}

__ begin_ And__ end_ Is a pointer, because const is very shallow__ The constant of X does not make the pointer const

Both are consistent because copyinsertable requires replicability from const and non const sources However, your template only hijacks copying from non const (because it loses the copy of const case by template / non template arbiter), so you can only see the problem in libc

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>