简体   繁体   English

无法在函数返回的不可复制容器上进行迭代

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

I'm not sure of the title, because I'm not sure the issue comes from the "copyablility" of my container. 我不确定标题,因为我不确定问题是否来自容器的“可复制性”。 I tryied quite everything but I can't get rid of this error. 我尝试了很多事情,但是我无法摆脱这个错误。

Here is a simplified version of my code (please do not challenge the class design, I really would like to keep the end-used syntax in the BOOST_FOREACH): 这是我的代码的简化版本(请不要挑战类设计,我真的很想将最终使用的语法保留在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;
}

The compiler complains about not having a copy constructor for MyContainer::MyContainer(const MyContainer&). 编译器抱怨没有MyContainer :: MyContainer(const MyContainer&)的副本构造函数。 I also have : error: no matching function for call to 'MyContainer::MyContainer(boost::foreach_detail_::rvalue_probe >::value_type)' 我也有:错误:没有匹配的函数可以调用'MyContainer :: MyContainer(boost :: foreach_detail _ :: rvalue_probe> :: value_type)'

I follow the extensibility tips: 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 我遵循可扩展性提示: 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

But, making 但是,

MyContainer<T> : private boost::noncopyable

doesn't solve the issue. 无法解决问题。 Nor defining the function 也没有定义功能

boost_foreach_is_noncopyable

or specializing the template struct 或专门化模板结构

is_noncopyable

for MyContainer (in fact, how would I specialize this template for a template type ?) for MyContainer(实际上,我该如何专门针对模板类型使用此模板?)

Last "tip" : If I remove the mutex and the lock from everywhere (I just pass the vector to GetContainer and to MyContainer), it works. 最后一个“技巧” :如果我从任何地方删除互斥锁和锁(我只是将向量传递给GetContainer和MyContainer),它就可以工作。 But it doesn't work if I make 但是如果我做的话那是行不通的

MyContainer<T> : private boost::noncopyable

(I expected it should, so I'm not sure my problem is with BOOST_FOREACH, but maybe because I return a copy of MyContainer with my getter ?) (我希望这样,所以我不确定我的问题出在BOOST_FOREACH上,但是可能是因为我用吸气剂返回了MyContainer的副本吗?)

I thank you if you read me until here, and thanks in advance for help. 如果您在此之前读过我的文章,我深表感谢,并先感谢您的帮助。

Seems to be a limitation of BOOST_FOREACH with move-only types. 似乎是仅移动类型对BOOST_FOREACH的限制。 I didn't find a way around it¹ (except for the - ugly - obvious approach to put the lock_guard in a shared_ptr ). 我没有找到解决方法的方法¹(除了-难看的-将lock_guard放在shared_ptr明显方法)。

You didn't specify a c++03 requirement, though, so you can make it work without BOOST_FOREACH by replacing lock_guard with unique_lock . 但是,您没有指定c ++ 03要求,因此可以通过用unique_lock替换lock_guard来使其在没有BOOST_FOREACH的情况下工作。

Here's my take on things in c++11 (note how generic it is): 这是我对c ++ 11的看法(请注意它的通用性):

Live On Coliru 生活在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;

}

Prints 打印

1
2
one
two
one

¹ not even using all the approaches from Using BOOST_FOREACH with a constant intrusive list ¹甚至没有使用将BOOST_FOREACH与常量插入列表一起使用时的所有方法

I post my answer if it can help... 如果有帮助,我会发布答案。

With C++03 , I finally provide a copy constructor to be able to use the class with BOOST_FOREACH. 使用C ++ 03 ,我最终提供了一个复制构造函数,使其能够与BOOST_FOREACH一起使用该类。 So the issue is moved to another topic: make the class copied in a logic and suitable way. 因此,问题转移到了另一个主题:以合理且适当的方式复制类。

In my case, I "share the lock and the vector", the user shouldn't use this copy itself if he doesn't want to do bugs, but in BOOST_FOREACH it's okay: 就我而言,我“共享锁和向量”,如果用户不想执行错误,则用户不应使用此副本本身,但在BOOST_FOREACH中可以:

  • I change the mutex to a recursive_mutex 我将互斥锁更改为recursive_mutex
  • I change the lock to an unique_lock and: 我将锁更改为unique_lock并:

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

With C++11 使用C ++ 11

Thanks to Chris Glover on the boost mailling list, a C++11 solution: 感谢Chris Glover在boost邮件列表中,这是一种C ++ 11解决方案:

You can't do what you are trying to do in C++03. 您无法执行C ++ 03中要执行的操作。 To accomplish it, you need C++11 move semantics to be able to move the MyContainer out of the Get function. 为此,您需要C ++ 11 move语义才能将MyContainer移出Get函数。 Even without using BOOST_FOREACH, the following code fails; 即使不使用BOOST_FOREACH,以下代码也会失败;

 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. 我将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