【问题标题】:How to use const InputIterators to write a function for generic containers?如何使用 const InputIterators 为通用容器编写函数?
【发布时间】:2015-05-19 16:43:55
【问题描述】:

我正在尝试了解如何编写一个(独立)函数,该函数采用 const InputIterators 并在迭代容器中的每个元素时执行一些简单的操作。为简单起见,我正在考虑一个简单的打印函数,它将打印[first..last) 之间范围内的所有元素,但不允许该函数修改容器中的元素。到目前为止,这是我所得到的:

#include<iostream>
#include<vector>
#include<list>

using namespace std;

template<class InputIterator>
void print(const InputIterator& first, const InputIterator& last)
{
  while (first != last) {
    cout << *first << " ";
    ++first;
  }
  cout << endl;
}


int main()
{
  vector<int> v;
  v.push_back(1);
  v.push_back(3);
  v.push_back(5);
  v.push_back(7);

  list<float> l;
  l.push_back(11.1);
  l.push_back(33.3);
  l.push_back(55.7);
  l.push_back(77.7);

  print(v.cbegin(), v.cend());
  print(l.cbegin(), l.cend());    
}

当我尝试编译代码时(使用带有 c++11 标志的 gcc4.7),我收到以下错误:

iterator.cpp: In instantiation of 'void print(const InputIterator&, const InputIterator&) [with InputIterator = __gnu_cxx::__normal_iterator<const int*, std::vector<int> >]':
iterator.cpp:32:29:   required from here
iterator.cpp:12:5: error: passing 'const __gnu_cxx::__normal_iterator<const int*, std::vector<int> >' as 'this' argument of '__gnu_cxx::__normal_iterator<_Iterator, _Container>& __gnu_cxx::__normal_iterator<_Iterator, _Container>::operator++() [with _Iterator = const int*; _Container = std::vector<int>; __gnu_cxx::__normal_iterator<_Iterator, _Container> = __gnu_cxx::__normal_iterator<const int*, std::vector<int> >]' discards qualifiers [-fpermissive]
iterator.cpp: In instantiation of 'void print(const InputIterator&, const InputIterator&) [with InputIterator = std::_List_const_iterator<float>]':
iterator.cpp:33:29:   required from here
iterator.cpp:12:5: error: passing 'const std::_List_const_iterator<float>' as 'this' argument of 'std::_List_const_iterator<_Tp>::_Self& std::_List_const_iterator<_Tp>::operator++() [with _Tp = float; std::_List_const_iterator<_Tp>::_Self = std::_List_const_iterator<float>]' discards qualifiers [-fpermissive]

我注意到,如果我删除了const 要求,将cbegincend 分别替换为beginend,代码可以正常工作。有人可以指出我在这里缺少什么吗?

【问题讨论】:

  • first 被声明为 const,但你尝试修改它:++first;
  • 你想要的是const_iterator而不是const iterator
  • 按值传递迭代器。

标签: c++ c++11 stl iterator constants


【解决方案1】:

您需要按值传递迭代器

template <class InputIterator>
void print(InputIterator first, InputIterator last)
{
    // as before
}

print 不会修改任何内容的合同是隐含的,因为您从未分配给*first

确实需要自己修改迭代器(以实际进行任何迭代),也就是说您不能通过const&amp; 获取它们(否则,您首先必须复制它们再次) - 但修改迭代器与修改底层内容不同。

【讨论】:

  • 我认为合约的名称为InputIterator,它是一个要读取的迭代器,而不是一个要写入的迭代器,存在OutputIterator
  • @Nawaz 这不是真正的合同——它只是一个命名约定。如果我们有概念,你可以做template &lt;InputIterator It&gt;,那么是的。
  • @Nawaz 名称根本不重要——它只是模板类型的占位符。您可以用 T 替换它,答案仍然有效。我以前也不知道:P
  • @Barry:我知道这只是一个名字。但它告诉 external 世界它不会修改内容,尽管目前编译器无法在编译时强制执行此操作,以查看任何违规行为。它被命名为InputIterator 是有原因的,而不仅仅是Iterator
  • @Barry 这是写下来的东西,作者 (A) 打算对另一方 (B) 提出的要求有特定含义,以及 A 打算对 B 做出承诺的东西如果 B 满足这些要求,A 将提供一个对 B 实际有用的功能。这与大多数其他称为合同的东西一样多。
【解决方案2】:

混淆似乎源于混合迭代器和迭代器指向的数据。

  • 可以改变迭代器,同时保持指向的只读。

让我们将这些概念映射到原始指针:

int buffer[ARRAY_SIZE];

const int *ptr2c = &(buffer[0]);

对比

int * const cptr = &(buffer[0]);

考虑使用这些指针中的任何一个循环遍历 'buffer' 的元素。使用“ptr2c”,应该能够遍历数组;无法通过此变量修改数组元素。

另一方面,'cptr',一个 const-pointer-to-int,允许你修改数组中指向的元素。但是,cptr 本身不能修改为指向任何其他位置。

映射回您的示例,“const InputIterator”使迭代器本身不可修改,这不是您想要的。这是粗略的,但是,您可以根据指针来可视化迭代器。当然,之后这些概念很快就会出现分歧。

【讨论】:

    猜你喜欢
    • 2021-04-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-12
    • 1970-01-01
    相关资源
    最近更新 更多