【问题标题】:Cannot iterate on a non-copyable container returned by a function无法迭代函数返回的不可复制容器
【发布时间】:2015-05-19 19:21:21
【问题描述】:

我不确定标题,因为我不确定问题来自我的容器的“可复制性”。 我尝试了几乎所有东西,但我无法摆脱这个错误。

这是我的代码的简化版本(请不要挑战类设计,我真的很想将最终使用的语法保留在 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

对于 MyContainer(事实上,我如何将这个模板专门用于模板类型?)

最后一个“提示”: 如果我从任何地方删除互斥锁和锁(我只是将向量传递给 GetContainer 和 MyContainer),它就可以工作。 但是如果我做它就行不通

MyContainer<T> : private boost::noncopyable

(我希望它应该如此,所以我不确定我的问题出在 BOOST_FOREACH 上,但可能是因为我用 getter 返回了 MyContainer 的副本?)

感谢您在此处阅读我的内容,并提前感谢您的帮助。

【问题讨论】:

  • 复制在 BOOST_FOREACH 中完成。问题是缺乏移动语义 AFAICT

标签: boost boost-foreach


【解决方案1】:

似乎是仅限移动类型的 BOOST_FOREACH 的限制。我没有找到解决方法¹(除了 - 丑陋 - 将lock_guard 放在shared_ptr 中的明显方法)。

不过,您没有指定 c++03 要求,因此您可以通过将 lock_guard 替换为 unique_lock 来使其在没有 BOOST_FOREACH 的情况下工作。

这是我对 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 中的所有方法

【讨论】:

  • 感谢您的回答。好的,BOOST_FOREACH 没办法。就我而言,我不能使用 c++11,即使我最终将锁放在 shared_ptr 中。这是相当丑陋的技术解决方法,但对于我的团队,我想保留 foreach 的简单语法。
【解决方案2】:

如果有帮助,我会发布我的答案...

使用 C++03,我终于提供了一个复制构造函数,以便能够将类与 BOOST_FOREACH 一起使用。 所以问题转移到另一个话题:让类以逻辑和合适的方式复制。

在我的情况下,我“共享锁和向量”,如果用户不想做错误,则不应使用此副本本身,但在 BOOST_FOREACH 中没关系:

  • 我将 mutex 更改为 recursive_mutex
  • 我将锁更改为 unique_lock 和:

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

使用 C++11

感谢 boost 邮件列表中的 Chris Glover,这是一个 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; 
    } 

【讨论】:

  • 呃。我想我在回答中为 C++11 提供了完全相同的修复程序(“因此,您可以通过将 lock_guard 替换为 unique_lock.”来使其在没有 BOOST_FOREACH 的情况下工作。”)。将互斥锁更改为递归是一项不平凡的更改,并且会引发与递归互斥锁相关的一系列问题。我喜欢这里的shared_ptr&lt;lock&gt;
  • @sehe 你是对的。我只是再次回答移动语义提示。对于with BOOST_FOREACH 的用法。但你的答案是我接受的+1 ;-)
猜你喜欢
  • 1970-01-01
  • 2022-10-02
  • 2016-08-17
  • 2021-12-09
  • 1970-01-01
  • 1970-01-01
  • 2023-04-01
  • 1970-01-01
  • 2019-08-10
相关资源
最近更新 更多