2015-05-19 2 views
1

Я не уверен в названии, потому что я не уверен, что проблема связана с «копийностью» моего контейнера. Я попробовал совсем все, но я не могу избавиться от этой ошибки.Невозможно выполнить итерацию в контейнере, не подлежащем копированию, возвращаемом функцией

Вот упрощенная версия моего кода (пожалуйста, не бросить вызов дизайн класса, я действительно хотел бы, чтобы конечный используемый синтаксис в 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 (повышение :: foreach_detail _ :: rvalue_probe> :: value_type)'

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

Но , что составляет

MyContainer<T> : private boost::noncopyable 

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

boost_foreach_is_noncopyable 

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

is_noncopyable 

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

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

MyContainer<T> : private boost::noncopyable 

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

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

+0

копия делается внутри BOOST_FOREACH. Проблема заключается в отсутствии семантики перемещения AFAICT – sehe

ответ

1

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

Однако вы не указали требование C++ 03, чтобы вы могли работать без BOOST_FOREACH, заменив lock_guard на unique_lock.

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

Live On Coliru

#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 

¹ не даже используя все подходы от Using BOOST_FOREACH with a constant intrusive list

+0

Спасибо за ваш ответ. Не используйте BOOST_FOREACH. В моем случае я не могу использовать C++ 11, и даже если, я, наконец, поставлю блокировку в shared_ptr. Это довольно безобразное техническое решение, но для моей команды я хочу сохранить простой синтаксис foreach. – Nico

0

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

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

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

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

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

С 11

C++ Благодаря Крис Гловер в списке подталкивание mailling, раствор C++ 11:

You can't do what you are trying to do in C++03. To accomplish it, you need C++11 move semantics to be able to move the MyContainer out of the Get function. Even without using BOOST_FOREACH, the following code fails;

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

Here's an example with the necessary changes; I changed the scoped_lock to a unique_lock and added a move constructor.

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; 
    } 
+0

Erm. Я думаю, что я поставил точно такое же исправление для C++ 11 в своем ответе (_ ", чтобы вы могли заставить его работать без BOOST_FOREACH, заменив' lock_guard' на 'unique_lock'._"). Изменение мьютекса для рекурсивности является нетривиальным изменением и предлагает множество проблем, связанных с рекурсивными мьютексами. Я бы одобрил 'shared_ptr ' здесь – sehe

+0

@, так как вы правы. Я просто отвечал снова за семантический совет. И для использования ** с ** BOOST_FOREACH. Но вы отвечаете, что я принял +1 от меня ;-) – Nico