【问题标题】:c++ how to create iterator over one field of a struct vectorc++ 如何在结构向量的一个字段上创建迭代器
【发布时间】:2015-02-03 02:53:30
【问题描述】:

我有一个所有原始类型的结构,如下所示:

struct record {
    int field1;
    double field2;
}

我有这个结构的实例向量,如下所示:

vector<record> records;

是否有可能/创建将迭代 field1vector&lt;int&gt;::iterator 的最佳方法是什么? 如果我使用数组record records[n] 会怎样?我需要看起来像 vector&lt;int&gt;::iterator 的东西。

编辑:我需要vector&lt;int&gt;::iterator

【问题讨论】:

  • 你看错了。在容器上创建一个迭代器,只在你想要的时候访问 field1
  • @B-Brock 你是想用这个向量搜索,还是什么?
  • 为什么需要这样的迭代器?您是否将其传递给另一个函数?如果是,该函数的要求是什么(指针、随机访问迭代器、向量迭代器?)
  • @BenVoigt 是的——完全正确。我想将它传递给的函数采用vector&lt;int&gt;::iterator 类型的beginend 迭代器。我已经在代码的其他部分使用了它,从实际的vector&lt;int&gt;s 传入迭代器。
  • 它总是相同的“类型”数据——对我来说,int 代表时钟跳跃,所以我正在考虑做一个继承的事情,但我真的不想做一个结构1 个字段,你知道吗?

标签: c++ vector struct iteration


【解决方案1】:

制作迭代器适配器

首先,最简单的解决方案是遍历容器并从迭代器中访问字段。

for (auto&& r : records) {
    int value = r.field1;
    /* do something with 'value' */
}

无论如何,如果您真的想要一个在取消引用时返回 field1 的迭代器,您可以轻松实现一个从容器自己的迭代器派生的迭代器适配器。

struct my_it : public std::vector<record>::iterator {
    using std::vector<record>::iterator::iterator;
    int operator*() { return std::vector<record>::iterator::operator*().field1; }
};

并像这样使用它:

for (my_it it = std::begin(records); it != std::end(records); ++it) {
    int value = *it; // Dereferencing now returns 'field1'.
}

是 XY 问题吗?

正如Ben Voigt 的回答中所解释的那样,除了存储在连续数组中的int 元素之外,没有办法创建一个std::vector&lt;int&gt;::iterator 来迭代其他东西。

如果您需要一个接受输入迭代器的函数,则将其设为 模板函数。这样,它将与 any 容器类型的迭代器一起使用。这就是标准库中所有算法的实现方式。

template <typename InputIt>
void func(InputIt first, InputIt last) {
    for (; first != last; ++first) {
        value = *it; // Dereferences input iterator of any type.
    }
}

迭代器应该通过它们的操作(即读取、递增、递减、随机访问)进行接口,而不是它们的显式类型。迭代器按其支持的操作数分类。

例如,如果您需要遍历一个范围并一次读取所有值,那么您希望 输入迭代器 作为参数。迭代器本身的类型应该是无关紧要的。

See this for more info about iterator categories

【讨论】:

  • 非常感谢您的建议!我需要它“看起来像”vector&lt;int&gt;::iterator
  • 我做了一个编辑——我应该说是vector&lt;int&gt;::iterator
  • 这很有帮助,但我认为` using std::vector::iterator::iterator; ` 在语法上不正确。迭代器没有定义 ::iterator。此外,我必须从 std::vector::iterator 为 struct my_it 定义一个构造函数才能使其工作。否则我无法从 std::begin(records) 创建 my_it。类似my_it(std::vector&lt;record&gt;::iterator it) { *(std::vector&lt;record&gt;::iterator*)this = it; }
【解决方案2】:

你运气不好。

vector&lt;int&gt;::iterator 不是多态的1。没有地方可以进入并更改指针步长。 vector&lt;int&gt;::iterator 仅迭代一系列连续 int 对象,而您的int 对象不会连续存储。

这就是为什么所有 C++ 标准算法都被模板化以接受任何类型的迭代器。如果你让你的函数成为接受任意迭代器类型的模板,你可以使用像 Snps 写的那样的迭代器适配器。


1多态性相对于指针算术,如果std::vector没有与普通数组类似的性能,没有人会使用它

【讨论】:

    【解决方案3】:

    您可以在原始 vector 上使用 lambda。

    例如:

    for_each(records.begin(), records.end(), [](record& foo){/*operate on foo.field1 here*/});
    

    请注意,绝大多数其他算法也接受 lambda,因此您只需使用 lambda 迭代原始 vector 即可访问 field1

    我在这里冒昧,但您正在寻找的行为似乎与 map 的行为非常相似,所以您可能想看看。

    我想你喜欢 Boost 和 evil,你也可以用这个来拼凑一些东西:http://www.boost.org/doc/libs/1_57_0/libs/range/doc/html/range/reference/adaptors/reference/strided.html

    【讨论】:

      【解决方案4】:

      看来你要的是这样的东西

      for(std::vector<record>::iterator i=records.begin(), end=records.end(); i!=end; ++i)
      {
          std::cout << i->field1 << std::endl;
      }
      

      或者在 C++11 中

      for(auto i=records.begin(), end=records.end(); i!=end; ++i)
      {
          std::cout << i->field1 << std::endl;
      }
      

      【讨论】:

        【解决方案5】:

        如果你真的想要一个成员迭代器,你可以这样做:

        template <class M>
        struct member_traits;
        
        template <class T, class C>
        struct member_traits<T C::*>
        {
            using class_type = C;
            using return_type = T;
        };
        
        template <class Iterator, class C, class M>
        struct member_iterator : Iterator
        {
        public:
            using Iterator::Iterator;
        
            template <class I, class Member>
            member_iterator(I&& begin, Member&& member)
                : std::vector<C>::iterator(std::forward<I>(begin))
                , member(std::forward<Member>(member))
            {
                static_assert(std::is_member_pointer<Member>::value,
                              "Member must be dereferenceable");
            }
        
            typename member_traits<M>::return_type& operator*()
            {
                return (*static_cast<Iterator&>(*this)).*member;
            }
        private:
            M member;
        };
        
        template <class Member, class Iterator>
        auto make_member_iterator(Member&& member, Iterator&& it)
            -> member_iterator<std::decay_t<Iterator>, typename member_traits<Member>::class_type, std::decay_t<Member>>
        {
            return {std::forward<Iterator>(it), std::forward<Member>(member)};
        }
        
        struct Record
        {
            int field1;
            double field2;
        };
        
        int main()
        {
            std::vector<Record> v { {1, 1.0}, {2, 2.0}, {3, 3.0} };
        
            for (auto it = make_member_iterator(&Record::field1, v.begin()); it != v.end(); ++it)
            {
                std::cout << *it << " ";
            }
        }
        

        Demo

        【讨论】:

        • return Iterator::operator*().*member; 这样使用碱基调用语法不是更传统吗?还是您担心operator* 可能不是会员?
        猜你喜欢
        • 1970-01-01
        • 2015-02-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-11-19
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多