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

前端之家收集整理的这篇文章主要介绍了使用包含不完整类型的`std :: vector`递归定义和访问`boost :: variant` – libstdc vs libc前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我试图使用 incomplete包装类和 std::vector作为我的间接技术来定义和访问“递归” boost::variant.我的实现适用于libstdc,但不适用于libc.

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

  1. struct my_variant_wrapper;
  2.  
  3. using my_variant_array = std::vector<my_variant_wrapper>; // <- indirection here
  4. using my_variant = boost::variant<int,my_variant_array>;
  5.  
  6. struct my_variant_wrapper
  7. {
  8. my_variant _v;
  9.  
  10. template <typename... Ts>
  11. my_variant_wrapper(Ts&&... xs) : _v(std::forward<Ts>(xs)...) { }
  12. };

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

我很有信心我可以使用std :: vector< my_variant_wrapper>,其中my_variant_wrapper是incomplete type,因为paper N4510 (“Minimal incomplete type support for standard containers”)

>根据WG21’s 2015 page,该论文获得批准.
>根据this page,libstdc一直支持这些功能.
>根据this page,它在libc 3.6中实现.

我随后访问该变体如下:

  1. struct my_visitor
  2. {
  3. void operator()(int x) const { }
  4. void operator()(const my_variant_array& arr) const
  5. {
  6. for(const auto& x : arr)
  7. boost::apply_visitor(*this,x._v);
  8. }
  9. };
  10.  
  11. int main()
  12. {
  13. my_variant v0 = my_variant_array{
  14. my_variant{1},my_variant{2},my_variant_array{
  15. my_variant{3},my_variant{4}
  16. }
  17. };
  18.  
  19. boost::apply_visitor(my_visitor{},v0);
  20. }

A minimal complete example is available on coliru.

>我使用以下标志:

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

> BOOST_VERSION的计算结果为106100.

代码

>按预期编译和运行:

> g(测试版本:6.1和7),libstdc.
> clang(测试版本:3.8),libstdc.
>(作为奖励,它也适用于std :: variant进行适当的更改!)

>无法编译:

> clang(测试版本:3.8,4),带有libc.

这是我在用libc编译时得到的错误

  1. In file included from prog.cc:2:
  2. In file included from /usr/local/boost-1.61.0/include/boost/variant.hpp:17:
  3. /usr/local/boost-1.61.0/include/boost/variant/variant.hpp:1537:28: error: no matching member function for call to 'initialize'
  4. initializer::initialize(
  5. ~~~~~~~~~~~~~^~~~~~~~~~
  6. /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
  7. convert_construct(operand,1L);
  8. ^
  9. 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
  10. my_variant_wrapper(Ts&&... xs) : _v(std::forward<Ts>(xs)...) { }
  11. ^
  12. /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
  13. ::new((void*)__p) _Up(_VSTD::forward<_Args>(__args)...);
  14. ^
  15. /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
  16. {__a.construct(__p,_VSTD::forward<_Args>(__args)...);}
  17. ^
  18.  
  19. ...

The full error is available on wandbox.

为什么代码没有用libc编译? (这可能是libc的N4510实现中需要报告的缺陷吗?)

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

解决方法

我在回溯中看到了这个:

note: in instantiation of function template specialization
my_variant_wrapper::my_variant_wrapper<my_variant_wrapper &>
requested here

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

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

实现之间的区别是由于vector的复制构造函数复制元素的方式. libstdc将源元素视为const:

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

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

libc将源元素视为非const:

  1. template <class _Tp,class _Allocator>
  2. vector<_Tp,_Allocator>::vector(const vector& __x)
  3. : __base(__alloc_traits::select_on_container_copy_construction(__x.__alloc()))
  4. {
  5. #if _LIBCPP_DEBUG_LEVEL >= 2
  6. __get_db()->__insert_c(this);
  7. #endif
  8. size_type __n = __x.size();
  9. if (__n > 0)
  10. {
  11. allocate(__n);
  12. __construct_at_end(__x.__begin_,__x.__end_,__n);
  13. }
  14. }

__begin_和__end_是指针,由于const很浅,__ x的常量不会使指针成为const.

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

猜你在找的Java相关文章