#c #boost #c 17 #libc #boost-variant
#c #boost #c 17 #libc #boost-variant
Вопрос:
Я пытаюсь определить и посетить «рекурсивный» boost::variant
, используя неполный класс-оболочку и std::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(Tsamp;amp;... xs) : _v(std::forward<Ts>(xs)...) { }
};
Я использую std::vector
для введения косвенности (чтобы динамическое распределение не позволяло my_variant
иметь бесконечный размер).
Я вполне уверен, что мне разрешено использовать std::vector<my_variant_wrapper>
, где my_variant_wrapper
неполный тип, из-за статьи N4510 («Минимальная неполная поддержка типов для стандартных контейнеров»):
-
Документ был одобрен в соответствии со страницей WG21 2015 года.
-
Согласно этой странице, функции всегда поддерживались в libstdc .
-
Это было реализовано в libc 3.6, согласно этой странице .
Затем я посещаю вариант следующим образом:
struct my_visitor
{
void operator()(int x) const { }
void operator()(const my_variant_arrayamp; arr) const
{
for(const autoamp; 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);
}
Минимально полный пример доступен на 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 .
Это ошибка, которую я получаю при компиляции на clang с 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(Tsamp;amp;... 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 amp;>' 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 amp;>' requested here
{__a.construct(__p, _VSTD::forward<_Args>(__args)...);}
^
...
Полная ошибка доступна в wandbox.
(Может ли это быть дефектом в реализации libc N4510, о котором необходимо сообщить? Почему код не компилируется с помощью libc ?)
Ошибка, по-видимому, предполагает, что варианту не удается определить, какие элементы следует инициализировать, но я, честно говоря, не мог в этом разобраться. Меня также смущает тот факт, что использование libstdc (с той же версией boost) работает так, как ожидалось.
Ответ №1:
Я видел это в обратном пути:
примечание: при создании экземпляра специализации шаблона функции «
my_variant_wrapper::my_variant_wrapper<my_variant_wrapper amp;>
запрошено здесь
что является явным признаком того, что ваш шаблон конструктора захватывает конструктор копирования.
Ограничьте его, и ваша проблема исчезнет.
Разница между реализациями обусловлена тем, как vector
конструктор копирования копирует элементы. libstdc обрабатывает исходные элементы как const
:
vector(const vectoramp; __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 vectoramp; x
, они возвращают постоянные итераторы.
libc обрабатывает исходные элементы как не- const
:
template <class _Tp, class _Allocator>
vector<_Tp, _Allocator>::vector(const vectoramp; __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 of __x
не имеет значения const
.
Оба соответствуют, поскольку CopyInsertable
требуется возможность копирования как из const
источников, так и из других const
источников. Однако ваш шаблон перехватывает копирование только из не- const
(потому что он теряет копирование из const
case с помощью шаблона / не шаблонного прерывателя), поэтому вы видите проблему только в libc .
Комментарии:
1. Потрясающе, спасибо! Он компилируется с libc при ограничении. Я все еще озадачен тем, почему эта ошибка возникает только с libc , хотя — не могли бы вы уточнить это?
2. @VittorioRomeo Просто потратил еще немного времени на копание — см. Редактирование.
3. Таким образом, вектор libc пытается скопировать-сконструировать векторные элементы, используя неконстантную ссылку lvalue, тогда как libstdc передает постоянную ссылку lvalue. Поскольку libc использует неконстантную ссылку, он выбирает конструктор шаблона вместо конструктора копирования.