簡體   English   中英

無法在函數返回的不可復制容器上進行迭代

[英]Cannot iterate on a non-copyable container returned by a function

我不確定標題,因為我不確定問題是否來自容器的“可復制性”。 我嘗試了很多事情,但是我無法擺脫這個錯誤。

這是我的代碼的簡化版本(請不要挑戰類設計,我真的很想將最終使用的語法保留在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#foreach.extensibility.making__literal_boost_foreach__literal__work_with_non_copyable_sequence_types

但是,

MyContainer<T> : private boost::noncopyable

無法解決問題。 也沒有定義功能

boost_foreach_is_noncopyable

或專門化模板結構

is_noncopyable

for MyContainer(實際上,我該如何專門針對模板類型使用此模板?)

最后一個“技巧” :如果我從任何地方刪除互斥鎖和鎖(我只是將向量傳遞給GetContainer和MyContainer),它就可以工作。 但是如果我做的話那是行不通的

MyContainer<T> : private boost::noncopyable

(我希望這樣,所以我不確定我的問題出在BOOST_FOREACH上,但是可能是因為我用吸氣劑返回了MyContainer的副本嗎?)

如果您在此之前讀過我的文章,我深表感謝,並先感謝您的幫助。

似乎是僅移動類型對BOOST_FOREACH的限制。 我沒有找到解決方法的方法¹(除了-難看的-將lock_guard放在shared_ptr明顯方法)。

但是,您沒有指定c ++ 03要求,因此可以通過用unique_lock替換lock_guard來使其在沒有BOOST_FOREACH的情況下工作。

這是我對c ++ 11的看法(請注意它的通用性):

生活在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

¹甚至沒有使用將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()) { } 

使用C ++ 11

感謝Chris Glover在boost郵件列表中,這是一種C ++ 11解決方案:

您無法執行C ++ 03中要執行的操作。 為此,您需要C ++ 11 move語義才能將MyContainer移出Get函數。 即使不使用BOOST_FOREACH,以下代碼也會失敗;

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

這是進行必要更改的示例; 我將scoped_lock更改為unique_lock並添加了move構造函數。

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM