Использование BOOST_FOREACH с постоянным навязчивым списком

Рассмотрим следующий код для перебора навязчивого списка с помощью макроса BOOST_FOREACH:

#include <boost/foreach.hpp>
#include <boost/intrusive/list.hpp>

typedef boost::intrusive::list<
    boost::intrusive::list_base_hook<> > MyList;

void iterate (const MyList& xs) {
    BOOST_FOREACH (MyList::const_reference node, xs);
}

int main () {
    MyList xs;
    iterate (xs);
    return 0;
}

В буст версии 1.48 код не работает с clang 3.2 (SVN) и gcc 4.6.3, но работает с gcc 4.5.3. С неконстантным параметром xs в iterate код работает. С включенным C++11 все компиляторы принимают код. При использовании boost-1.46 обе версии gcc принимают код, а clang - нет.

Является ли данный код неправильным использованием BOOST_FOREACH макрос, или ошибка на стороне повышения? Есть ли обходной путь, который лучше, чем итерация с обычным циклом for?

Редактировать: я вставил сообщения об ошибках в pastebin (оба очень подробные) для GCC и clang.

2 ответа

Решение

Вот что я мог почерпнуть из журналов, а также из моих выводов о причине сбоя.

Короткая версия: по какой-то причине BOOST_FOREACH пытается скопировать данные, что невозможно.

На странице " Расширяемость" есть примечание:

Изготовление BOOST_FOREACH Работа с не копируемыми типами последовательностей

Для типов последовательностей, которые не могут быть скопированы, нам нужно указать BOOST_FOREACH не пытаться делать копии. Если наш тип наследует от boost::noncopyable никаких дальнейших действий не требуется. Если нет, мы должны специализировать boost::foreach::is_noncopyable<> шаблон [...] Еще один способ добиться того же эффекта - переопределить глобальный boost_foreach_is_noncopyable() функция. Делая это таким образом, вы получаете преимущество переносимости на старые компиляторы.

Исходя из диагноза, неясно, правильно ли настроен тип, поэтому вы можете попробовать.


Подрезанный диагноз и анализ.

/usr/include/boost/foreach.hpp:571:37: error: no matching constructor for initialization of 'boost::intrusive::list< >'
        ::new(this->data.address()) T(t);
                                    ^ ~
/usr/include/boost/foreach.hpp:648:51: note: in instantiation of member function 'boost::foreach_detail_::simple_variant<boost::intrusive::list< > >::simple_variant' requested here
    return auto_any<simple_variant<T> >(*rvalue ? simple_variant<T>(t) : simple_variant<T>(&t));
                                                  ^

/usr/include/boost/intrusive/list.hpp:1490:35: note: candidate constructor not viable: 1st argument ('const boost::intrusive::list< >') would lose const qualifier
   BOOST_MOVABLE_BUT_NOT_COPYABLE(list)
                                  ^
/usr/include/boost/move/move.hpp:371:7: note: expanded from macro 'BOOST_MOVABLE_BUT_NOT_COPYABLE'
      TYPE(TYPE &);\

/usr/include/boost/intrusive/list.hpp:1497:4: note: candidate constructor not viable: no known conversion from 'const boost::intrusive::list< >' to 'const value_traits' (aka 'const boost::intrusive::detail::base_hook_traits<boost::intrusive::list_base_hook< >, boost::intrusive::list_node_traits<void *>, 1, boost::intrusive::default_tag, 1>') for 1st argument; 
   list(const value_traits &v_traits = value_traits())
   ^
/usr/include/boost/intrusive/list.hpp:1506:4: note: candidate constructor not viable: no known conversion from 'const boost::intrusive::list< >' to '::boost::rv<list< >> &' for 1st argument; 
   list(BOOST_RV_REF(list) x)
   ^
/usr/include/boost/intrusive/list.hpp:1502:4: note: candidate constructor template not viable: requires at least 2 arguments, but 1 was provided
   list(Iterator b, Iterator e, const value_traits &v_traits = value_traits())
   ^

Я пытался максимально локализовать ошибку (удаление следов и т. Д.) Видимо, проблема связана с boost::intrusive::list а точнее невозможность построить новый boost::intrusive::list<> из boost::intrusive::list<> const,

Самый многообещающий конструктор определяется макросом:

BOOST_MOVABLE_BUT_NOT_COPYABLE(list)

который расширяется до

list(list&);

Именно так Boost эмулирует семантику перемещения для не копируемых типов в C++03. Однако он не может двигаться от const пункт с const квалификатор будет потерян.

Это выглядит как часть хитрости, используемой BOOST_FOREACH чтобы избежать многократной оценки аргумента контейнера (в случае, если это вызов функции), хотя я немного удивлен, что он пытается скопировать аргумент здесь.

Поскольку вы используете gcc > 4.6 и clang 3.2, вы можете использовать циклы C++11 для циклов:

#include <boost/foreach.hpp>
#include <boost/intrusive/list.hpp>

typedef boost::intrusive::list<
    boost::intrusive::list_base_hook<> > MyList;

void iterate (const MyList& xs) {
    for(const auto &node : xs) {
        // do something with node
    }
}

int main () {
    MyList xs;
    iterate (xs);
    return 0;
}

Вы также можете использовать std::for_each:

void iterate (const MyList& xs) {
    std::for_each(xs.begin(), xs.end(), 
      [](MyList::const_reference node) {
         // do something with node
      }
    );
}
Другие вопросы по тегам