Невозможно выполнить итерацию для не копируемого контейнера, возвращаемого функцией

Я не уверен в названии, потому что я не уверен, что проблема связана с "копируемостью" моего контейнера. Я попробовал все, но не могу избавиться от этой ошибки.

Вот упрощенная версия моего кода (пожалуйста, не оспаривайте дизайн класса, я действительно хотел бы сохранить синтаксис конечного использования в BOOST_FOREACH):

template <typename T>
class MyContainer
{
public:
    typedef typename std::vector<T>::iterator iterator;
    typedef typename std::vector<T>::const_iterator const_iterator;

    MyContainer(std::vector<T>& vec, boost::mutex& mutex) :
        m_vector(vec),
        m_lock(mutex)
    {
    }

    iterator begin() { return m_vector.begin(); }
    const_iterator begin() const { return m_vector.begin(); }
    iterator end() { return m_vector.end(); }
    const_iterator end() const { return m_vector.end(); }


private:
    std::vector<T>& m_vector;
    boost::lock_guard<boost::mutex> m_lock;
};

template <typename T>
struct GetContainer
{
    GetContainer(std::vector<T>& vec, boost::mutex& mutex) :
        m_vector(vec),
        m_mutex(mutex)
    {
    }

    MyContainer<T> Get()
    {
        return MyContainer<T>(m_vector, m_mutex);
    }

    std::vector<T>& m_vector;
    boost::mutex& m_mutex;
};



int main()
{
    std::vector<int> v;
    v.push_back(1);
    v.push_back(2);
    boost::mutex m;

    GetContainer<int> getter(v, m);

    BOOST_FOREACH(int i, getter.Get())
    {
        std::cout << i << std::endl;
    }

    return 0;
}

Компилятор жалуется на отсутствие конструктора копирования для MyContainer::MyContainer(const MyContainer&). У меня также есть: ошибка: нет подходящей функции для вызова MyContainer::MyContainer(boost::foreach_detail_::rvalue_probe >::value_type) '

Я следую советам по расширяемости: http://www.boost.org/doc/libs/1_58_0/doc/html/foreach/extensibility.html

Но, делая

MyContainer<T> : private boost::noncopyable

не решает проблему. Не определяя функцию

boost_foreach_is_noncopyable

или специализирующаяся структура шаблона

is_noncopyable

для MyContainer (на самом деле, как бы я специализировал этот шаблон для типа шаблона?)

Последний "совет": если я удаляю мьютекс и блокировку отовсюду (я просто передаю вектор в GetContainer и MyContainer), это работает. Но это не сработает, если я сделаю

MyContainer<T> : private boost::noncopyable

(Я ожидал, что так и должно быть, поэтому я не уверен, что моя проблема связана с BOOST_FOREACH, но, возможно, потому, что я возвращаю копию MyContainer с моим геттером?)

Я благодарю вас, если вы читаете меня до сих пор, и заранее спасибо за помощь.

2 ответа

Решение

Кажется, ограничение BOOST_FOREACH для типов только для перемещения. Я не нашел способа обойти это (за исключением - уродливого - очевидного подхода к lock_guard в shared_ptr).

Вы не указали требование C++03, поэтому вы можете заставить его работать без BOOST_FOREACH, заменив lock_guard с unique_lock,

Вот мой взгляд на вещи в C++11 (обратите внимание, насколько это универсально):

Жить на Колиру

#include <boost/thread.hpp>
#include <boost/range.hpp>

namespace detail {
    template <typename R, typename M>
    struct RangeLock {
        RangeLock(R&r, M& m) : _r(r), _l(m) {}
        RangeLock(RangeLock&&) = default;

        using iterator = typename boost::range_iterator<R>::type;
        iterator begin() { using std::begin; return begin(_r); }
        iterator end  () { using std::end;   return end  (_r); }

        using const_iterator = typename boost::range_iterator<R const>::type;
        const_iterator begin() const { using std::begin; return begin(_r); }
        const_iterator end  () const { using std::end;   return end  (_r); }

     private:
        R& _r;
        boost::unique_lock<M> _l;
    };
}

template <typename R, typename M>
    detail::RangeLock<R,M> make_range_lock(R& r, M& mx) { return {r,mx}; }
template <typename R, typename M>
    detail::RangeLock<R const,M> make_range_lock(R const& r, M& mx) { return {r,mx}; }

#include <vector>
#include <map>

int main() {

    boost::mutex mx;

    std::vector<int> const vec { 1, 2 };
    std::map<int, std::string> const map { { 1, "one" }, { 2, "two" } };

    for(int i : make_range_lock(vec, mx))
        std::cout << i << std::endl;

    for(auto& p : make_range_lock(map, mx))
        std::cout << p.second << std::endl;

    for(auto& p : make_range_lock(boost::make_iterator_range(map.equal_range(1)), mx))
        std::cout << p.second << std::endl;

}

Печать

1
2
one
two
one

¹ даже не используя все подходы из использования BOOST_FOREACH с постоянным навязчивым списком

Я отправлю свой ответ, если это может помочь...

В C++03 я наконец предоставил конструктор копирования, чтобы иметь возможность использовать класс с BOOST_FOREACH. Таким образом, проблема перенесена в другую тему: сделать класс копируемым логичным и подходящим способом.

В моем случае, я "делюсь блокировкой и вектором", пользователь не должен использовать эту копию сам, если он не хочет делать ошибки, но в BOOST_FOREACH все в порядке:

  • Я меняю мьютекс на recursive_mutex
  • Я изменяю блокировку на unique_lock и:

    MyContainer(const MyContainer& other) :
                                m_vector(other.vec),
                                m_lock(*other.m_lock.mutex())
    {
    }
    

С С ++11

Спасибо Крису Гловеру из списка поддержки, решение C++11:

Вы не можете делать то, что пытаетесь сделать в C++03. Для этого вам понадобится семантика перемещения C++11, чтобы можно было вывести MyContainer из функции Get. Даже без использования BOOST_FOREACH следующий код завершается ошибкой;

GetContainer<int> getter(v, m); 
MyContainer<int> c = getter.Get(); // <-- Error. 

Вот пример с необходимыми изменениями; Я изменил scoped_lock на unique_lock и добавил конструктор перемещения.

template <typename T> 
class MyContainer 
{ 
public: 
[...]
    MyContainer(MyContainer&& other) 
        : m_vector(other.m_vector) 
    { 
        m_lock = std::move(other.m_lock); 
        other.m_vector = nullptr; 
    } 
Другие вопросы по тегам