Возможная ошибка в повышении посещаемости

У меня есть следующее сообщение об ошибке:

/usr/include/boost/variant/detail/visitation_impl.hpp:207: typename Visitor::result_type boost::detail::variant::visitation_impl(int, int, Visitor &, VPCV, mpl::true_, NBF, W *, S *) [W = mpl_::int_<20>, S = boost::detail::variant::visitation_impl_step<boost::mpl::l_iter<boost::mpl::l_end>, boost::mpl::l_iter<boost::mpl::l_end> >, Visitor = boost::detail::variant::copy_into, VPCV = const void *, NBF = boost::variant<TypeInfo, int, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_>::has_fallback_type_]: Assertion `false' failed.

Это происходит, когда я передаю std::vector<A> в качестве параметра по значению какой-либо функции, а A определяется как using A = boost::variant<B, int>;. Для простоты B определяется следующим образом:

class B
{
    Data data;
    std::vector< boost::variant<std::shared_ptr<C>, B> > vec;
};

B вместо TypeInfo в сообщении об ошибке.

void func(std::vector<B> vec); //signature

auto result = func(that_vector_with_variants); //that line causes an error

Я нашел похожую ошибку здесь https://svn.boost.org/trac/boost/ticket/5146

Мой вопрос: это ошибка в boost? Как заставить мой код работать?

Обновлять:

Думаю что надо добавить, что если поменять std::vector<boost::variant<std::shared_ptr<C>, B> > vec; на std::vector<boost::variant<C*, B> > vec; то все работает.


person justanothercoder    schedule 16.01.2015    source источник
comment
Что за посетитель? Вы не показываете никакого связанного кода. Вы используете вариант. В контейнере. Хорошо. Это в основном все, что мы знаем на данный момент   -  person sehe    schedule 16.01.2015
comment
@sehe, главная проблема в том, что это какой-то внутренний гость. Когда я смотрю трассировку в gdb, я вижу, что при копировании вектора возникла ошибка.   -  person justanothercoder    schedule 16.01.2015


Ответы (1)


Выйдя на риск и прочитав этот отчет об ошибке, вы, возможно, непреднамеренно сделали что-то вроде этого:

#include <iostream>
#include <boost/make_shared.hpp>
#include <boost/variant.hpp>

using P = boost::shared_ptr<int>;
using V = boost::variant<P, int>;
using C = std::vector<V>;

int main() {
    P p = boost::make_shared<int>(42);
    assert(p.unique());

    C v = { p };
    assert(!p.unique());

    v = std::move(v);
    assert(!p.unique()); // WHOOPS assert fails
}

Конечно, этот пример надуманный², потому что я не знаю вашего фактического кода/варианта использования.

Последнее утверждение не выполняется. Это связано с тем, что внутренний порядок операций таков, что исходный вариант очищается (и содержащееся в нем значение уничтожается) до присвоения нового значения:

Гарантия «никогда не пустая»

Хотя никогда непустая гарантия может на первый взгляд показаться «очевидной», на самом деле даже неясно, как ее реализовать в целом.

См. «Идеальное» решение: ложные надежды из варианта Boost «Обзор дизайна»

Таким образом, если бы у нас не было «ссылки» на int в p, эта операция уничтожила бы экземпляр до того, как он был переназначен.

В этом случае это больше похоже на шаблон использования (по сути, модификация на месте с помощью интеллектуальных указателей ¹), которого следует избегать. В этом конкретном случае они работают (в моей реализации компилятора/библиотеки):

std::move(v.begin(), v.end(), v.begin());
// or
std::copy(std::make_move_iterator(v.begin()), std::make_move_iterator(v.end()), v.begin());
// or
v.assign(std::make_move_iterator(v.begin()), std::make_move_iterator(v.end()));

Но я бы конечно предпочел написать это больше похоже на

C tmp = std::move(v); 
v = std::move(tmp);

Просто потому, что нет никаких гарантий в противном случае.


¹ всегда опасность! Даже Скотт Мейерс чувствует это в своей книге «Более эффективный C++», см. ошибки стр. 200/202

² связанные: Что делает стандартная библиотека гарантия о назначении самостоятельного перемещения?

person sehe    schedule 16.01.2015
comment
Кстати, что такое std::move(v.begin(), v.end(), v.begin());? такой перегрузки нет. - person Nawaz; 10.08.2016
comment
Упс. Я никогда этого не знал. спасибо, что снова научили меня чему-то новому. - person Nawaz; 11.08.2016