2016-10-13 2 views
6

Я пытаюсь определить и посетить «рекурсивный» boost::variant используя класс incomplete обертку и std::vector как мои методы косвенности. Моя реализация работает с libstdC++, но не с libC++.Рекурсивного определение и посещение `подталкивания :: variant` с помощью` зОго :: VECTOR`, содержащего неполный типа - 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_wrapper является incomplete type, из-за paper N4510("Minimal incomplete type support for standard containers"):

  • Документ был утвержден, в соответствии с WG21's 2015 page.

  • Характеристики всегда поддерживались в libstdC++, в соответствии с this page.

  • Это было реализовано в libC++ 3.6, в соответствии с this page.


Я тогда посещение вариант следующим образом:

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 = С ++ 1z -Wall -Wextra -Wpedantic

  • BOOST_VERSION вычисляет 106100.

Код:

  • компилируется и работает по назначению на:

    • г ++ (протестированных версиях: 6.1 и 7) с libstdC++.

    • clang ++ (проверенные версии: 3.8), с libstdC++.

    • (в качестве бонуса, он также работает с std::variant путем внесения соответствующих изменений!)

  • Не удается скомпилировать на:

    • лязгом ++ (протестированных версий : 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.


Почему код не компиляции с LIBC++?(Может быть, это дефект в реализации LibC++ ы N4510, который должен сообщать?)

ошибка, кажется, предполагает, что вариант не удается обнаружить то, что члены должны быть инициализированы, но я честно не мог сделать много смысла из этого. Меня также смущает тот факт, что с использованием libstdC++(с той же форсированной версией) работает должным образом.

ответ

6

Я видел это в трассировку:

примечание: в конкретизации функции специализации шаблона «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 неглубокий, то const -ness из __x не делает pointee const.

Оба соответствуют друг другу, поскольку CopyInsertable требует возможности перераспределения как от const, так и от const источников. Тем не менее, ваш шаблон только захватывает копирование с не const (потому что он теряет копирование из const с помощью шаблона/без шаблона tiebreaker), поэтому вы видите проблему только в libC++.

+0

Awesome, спасибо! [Он компилируется с ** libC++ **] (http://melpon.org/wandbox/permlink/TCBfoFPrEZHFE8G0) при ограничении. Я все еще озадачен тем, почему эта ошибка возникает только с libC++, хотя, может быть, вы прокомментируете это? –

+1

@ VittorioRomeo Просто потратил некоторое время на рытье - см. Править. –

+1

Итак, вектор libC++ пытается скопировать-построить векторные элементы, используя ссылку на константу lvalue, тогда как libstdC++ передает ссылку const lvalue. Поскольку libC++ использует неконстантную ссылку, он выбирает конструктор шаблонов вместо конструктора копирования. – EricWF

Смежные вопросы