Трава представляет способ циклы через вектор:
for(vector<int>::iterator i = v.begin(); i < v.end(); i++) {
cout << *i << endl;
}
Он заменяет этот код:
copy(v.begin(), v.end(), ostream_iterator<int>(cout, "\n"));
Я изо всех сил пытаюсь понять, как, или почему это работает. Я просмотрел функцию копирования, и в документации говорится, что она эквивалентна:
template<class InputIterator, class OutputIterator>
OutputIterator copy (InputIterator first, InputIterator last,
OutputIterator result)
{
while (first!=last) {
*result = *first;
++result; ++first;
}
return result;
}
Поэтому я задал вопрос: "Что происходит, когда мы * OutputIterator?"
reference operator*() const;
Dereference iterator
Returns *this.
И тут я смутился. Я не вижу определения того, что указывает OutputIterator. Кроме того, я не вижу, как строка *result = *first;
возможно, перевести на вызов cout << *i;
Вы только посмотрели, что делает OutputIterator
. OutputIterator
- это всего лишь набор требований, с которыми OutputIterator
множество типов в стандартной библиотеке. Одним из таких типов является std::ostream_iterator
, поэтому вам нужно посмотреть, как это происходит в контексте std::copy
.
Поэтому в алгоритме копирования мы *result = *first
делаем *result = *first
. Во-первых, operator*
для std::ostream_iterator
ничего не делает - он просто возвращает сам итератор. Магия происходит, когда мы назначаем этот итератор. Если вы посмотрите на std::ostream_iterator::operator=
, вы увидите, что назначение этого итератора будет вставляться (используя <<
) в поток, с которым он был построен. Таким образом, назначение в вашем случае будет передано в std::cout
.
После этого оба result
и first
увеличиваются. Приращение result
(std::ostream_iterator
) не влияет, и приращение first
переместится к следующему элементу вектора. Затем в следующей итерации этот следующий элемент снова вставлен в std::cout
и т.д.
Как вы можете видеть, std::ostream_iterator
действительно не ведет себя так, как вы ожидали, что типичный итератор будет вести себя (перемещение по последовательности элементов, где выполнение косвенности на них дает вам текущий элемент). Однако он удовлетворяет требованиям OutputIterator
и поэтому может использоваться как один.
Здесь выполняется реализация std::ostream_iterator::operator=
из libstdc++:
/// Writes @a value to underlying ostream using operator<<. If
/// constructed with delimiter string, writes delimiter to ostream.
ostream_iterator&
operator=(const _Tp& __value)
{
__glibcxx_requires_cond(_M_stream != 0,
_M_message(__gnu_debug::__msg_output_ostream)
._M_iterator(*this));
*_M_stream << __value;
if (_M_string) *_M_stream << _M_string;
return *this;
}
Игнорируя утверждение в первой строке, мы видим, что он затем вставляет значение __value
во внутренний поток _M_stream
. Затем, если есть разделитель, _M_string
, он также вставлен в _M_stream
. Затем он возвращается.
for (const auto& obj: myObjVector) { std::cout << obj << std::endl; }
предпочтительнее из-за своей простоты.