首页 文章

使用包含不完整类型的`std :: vector`递归定义和访问`boost :: variant` - libstdc vs libc

提问于
浏览
6

我正在尝试使用incomplete包装类和std::vector作为我的间接技术来定义和访问"recursive" boost::variant . 我的实现适用于 libstdc++ ,但不适用于 libc++ .


这是我定义我的变体的方式:

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)...) { }
};

我使用 std::vector 来引入间接(因此动态分配将阻止 my_variant 具有无限大小) .

我很有信心我被允许使用 std::vector<my_variant_wrapper> ,其中 my_variant_wrapperincomplete type,因为paper N4510 ("Minimal incomplete type support for standard containers")

根据this page

  • libc++ 3.6 实施 .

我随后访问变体如下:

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.


  • 我正在使用以下标志:

-std = c 1z -Wall -Wextra -Wpedantic

  • BOOST_VERSION 评估为 106100 .

代码:

意图

  • Compiles and runs

  • g(测试版本:6.1和7), libstdc++ .

  • clang(测试版本:3.8), libstdc++ .

  • (作为奖励,通过进行适当的更改,它也适用于 std::variant !)

  • Fails to compile 上:

  • clang(测试版本:3.8,4), libc++ .


这是我在使用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++? (这可能是libc的N4510实现中需要报告的缺陷吗?)

该错误似乎表明该变体未能检测到应该初始化哪些成员,但我真的无法理解它 . 我也对使用 libstdc++ (具有相同的boost版本)按预期工作的事实感到困惑 .

1 回答

  • 7

    我在回溯中看到了这个:

    注意:在实例化函数模板特化'my_variant_wrapper :: my_variant_wrapper <my_variant_wrapper&>'这里要求

    这清楚地表明您的构造函数模板正在劫持复制构造函数 .

    限制它,你的问题就消失了 .


    实现之间的区别是由于 vector 的复制构造函数复制元素的方式 . libstdc将源元素视为 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());
    }
    

    因为 begin()end()const vector& x 上被调用,所以它们返回常量迭代器 .

    libc将源元素视为非 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___end_pointer s,并且由于 const 浅, __xconst -ness不会成为指针 const .

    两者都符合要求,因为 CopyInsertable 要求来自 const 和非 const 来源的可复制性 . 但是,您的模板仅劫持从非 const 复制(因为它失去了模板/非模板仲裁器从 const 案例中复制),因此您只能在libc中看到问题 .

相关问题