【问题标题】:c++ Range_Based Loop with custom Object [duplicate]带有自定义对象的c ++ Range_Based循环[重复]
【发布时间】:2018-09-12 23:07:26
【问题描述】:

我有两个看起来像这样的对象:

class Object
{
};

class List
{
public:
    std::vector<std::shared_ptr<Object>> list;
};

我希望能够像这样创建一个基于范围的循环:

for (Object& object : list) {
    //...
}

为此我创建了 begin() 和 end() 成员:

class List
{
public:

    std::vector<std::shared_ptr<Object>> list;

    Object& begin() { return *list.begin()->get(); }
    const Object& begin() const { return *list.begin()->get(); }
    Object& end() { return *list.end()->get(); }
    const Object& end() const { return *list.end()->get(); }
};

但是 IDE 现在要求我提供运算符 *、++ 和 !=,我不明白我应该如何实现,我能得到一些帮助吗?

【问题讨论】:

  • 您没有提供有意义的beginend。它们应该返回迭代器,而不是对象。此外,取消引用 end 迭代器是 UB,但您不需要那段代码。
  • @LogicStuff 那么我如何将 shared_ptr 转换为参考?
  • @Dolfos 您需要为List 编写一个迭代器类型。尝试将您的 shared_ptrs 转换为引用而不是使用迭代器类型是错误的。

标签: c++ object foreach


【解决方案1】:

range-for 协议要求 begin()end() 返回符合标准库的 Iterator 概念的类型。这包括能够使用operator* 取消引用迭代器,并使用operator++ 递增它。

begin(), 返回的对象引用不满足这些要求,这是编译器抱怨的。

编写自己的迭代器非常乏味,尤其是如果您想支持std::vector 的迭代器提供的所有随机访问功能。不过,有些库可以使这变得更容易一些。

一种更直接的方法是简单地转发您的list 成员已经提供的迭代器,即

class List
{
public:
    using iterator = std::vector<shared_ptr<Object>>::iterator;
    using const_iterator = std::vector<shared_ptr<Object>>::const_iterator;

    std::vector<std::shared_ptr<Object>> list;

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

现在您可以在 range-for 循环中使用 List,方法是:

for (auto& optr : list) {
    do_something_with_object(*optr);
}

唯一的问题是取消引用迭代器会给你一个shared_ptr&lt;Object&gt;的引用,而不是Object&amp;,所以你需要再次取消引用它才能真正使用对象本身(如上所述)。

【讨论】:

    【解决方案2】:

    基于范围的 for 循环需要的不仅仅是begin()end() 返回的一些任意对象。

    for (auto& object : list) { ... }
    

    对于您的列表,per definition 或多或少等同于以下内容:

    {
        auto iterator = list.begin();
        auto end = list.end();
        for ( ; iterator != end; ++iterator)
        {
            auto& object = *iterator;
            ...
        }
    }
    

    这就是为什么您的编译器会要求您执行所有这些操作(!=++*),因为它会尝试将它们应用于begin() 返回的对象。

    问题是您需要提供iterator,而不仅仅是列表的第一个和最后一个元素。你不能给编译器两个不同的Objects 并期望它能够循环遍历指向它们的指针列表(这在循环中不一定可见)。

    特里斯坦在他的回答中为您提供了一些选项。我要补充一点,ranges library 可以消除编写你自己的迭代器的许多乏味,但它很可能对你的情况来说是多余的,而且可能不是最容易习惯的。

    【讨论】:

      【解决方案3】:

      如果你掌握了以前的回复,我会用简短的​​ sn-p 重写它们。不需要任何 typedef:

      class List {
      public:
          std::vector<std::shared_ptr<Object>> list;
          auto begin() { return list.begin(); }
          auto begin() const { return list.begin(); }
          auto end() { return list.end(); }
          auto end() const { return list.end(); } 
      };
      

      【讨论】:

        猜你喜欢
        • 2017-03-02
        • 2021-05-11
        • 1970-01-01
        • 2017-02-20
        • 2021-07-25
        • 1970-01-01
        • 2020-10-04
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多