на основе диапазона для в C ++ 11 - PullRequest
10 голосов
/ 25 января 2012

в c ++ 11, если у нас есть set<int> S; мы могли бы сказать:

for (auto i: S)
    cout << i << endl;

но можем ли мы заставить i быть итератором, я имею в виду написать код, эквивалентный:

for (auto i = S.begin(); i != S.end(); i++)
    cout << (i != s.begin()) ? " " : "" << *i;

или мы можем сделать что-то, что мы можем понять, индекс i в наборе (или в векторе)?

и другой вопрос: как мы можем сказать, что это делается не для всех элементов в S, а для первой половины или всех из них, кроме первого.

или когда у нас есть vector<int> V, и мы хотим напечатать его первые n значения, что нам делать? Я знаю, что мы можем создать новый вектор, но для копирования вектора в новый вектор требуется время.

Ответы [ 6 ]

20 голосов
/ 25 января 2012

Нет, к несчастью. Посмотрите, что говорится в стандарте:

Диапазон на основе для выписки для (для-диапазон-объявление: выражение) эквивалентно

{
    auto && __range = ( expression );
    for ( auto __begin = begin-expr, __end = end-expr; __begin != __end; ++__begin ) {
        for-range-declaration = *__begin;
        statement
    }
}

где __range, __begin и __end - переменные, определенные только для экспозиции

Другими словами, он уже повторяется с begin до end и уже разыменовывает итератор, который вы никогда не увидите.

9 голосов
/ 25 января 2012

Принцип, основанный на диапазоне for, состоит в том, чтобы выполнять итерацию по всему диапазону.

Однако вы решаете, что такое диапазон, поэтому вы можете работать с самим диапазоном.

template <typename It>
class RangeView {
public:
  typedef It iterator;

  RangeView(): _begin(), _end() {}
  RangeView(iterator begin, iterator end): _begin(begin), _end(end) {}

  iterator begin() const { return _begin; }
  iterator end() const { return _end; }

private:
  iterator _begin;
  iterator _end;
};

template <typename C>
RangeView<typename C::iterator> rangeView(C& c, size_t begin, size_t end) {
  return RangeView<typename C::iterator>(
           std::next(c.begin(), begin),
           std::next(c.begin(), end)
         );
}

template <typename C>
RangeView<typename C::const_iterator> rangeView(C const& c, size_t begin, size_t end) {
  return RangeView<typename C::const_iterator>(
           std::next(c.begin(), begin),
           std::next(c.begin(), end)
         );
}

Хорошо, этот серьезно повторяющийся символ Boost.Range ...

А теперь давайте использовать его!

for (auto i: rangeView(set, 1, 10)) {
  // iterate through the second to the ninth element
}
2 голосов
/ 25 января 2012

В общем случае вам придется использовать отдельную переменную:

int i = 0;

for (auto x : s)
    cout << (i++ ? " " : "") << x << endl;

Конечно, есть определенные приемы для определенных контейнеров, таких как vector, но ни один не работает для каждого контейнера.

Возможно, для этой цели было бы лучше использовать простой цикл for.

2 голосов
/ 25 января 2012

Вы не можете в set.Используйте традиционный синтаксис for или сохраните свой собственный счетчик индекса.

Вы можете в vector или другом контейнере с плоской разметкой, такой как std::array или массив в стиле C,Измените его на , используйте ссылку. :

for (auto &i: S)

Затем вы можете сравнить адрес i с адресом s[0], чтобы получить индекс.

2 голосов
/ 25 января 2012

Нет, вы не можете.

for (... : ...)

называется for вместо foreach только по причине отсутствия ввода нового ключевого слова.Весь смысл foreach - это быстрый короткий синтаксис для итерации всех элементов без учета их индекса.Для всех остальных ситуаций есть простой for, который достаточно эффективно выполняет свою задачу.

0 голосов
/ 25 января 2012

Range-based for предназначен для простых случаев. Я ожидал, что он будет немного полезен при создании прототипа чего-либо, но я ожидаю, что его использование в основном прошло задолго до того, как все стало продуктом. Возможно, это может быть полезно, чтобы облегчить жизнь новичкам, но об этой области я не могу судить (но то, что, кажется, движет многими недавними дискуссиями на C ++).

Единственным конструктивным подходом может быть использование адаптера, который ссылается на базовый диапазон и чьи методы begin() и end() корректируют итератор соответствующим образом. Также обратите внимание, что вы, вероятно, хотите поднять любую особую обработку первого или последнего элемента из цикла, обрабатывающего большую часть данных. Конечно, это всего лишь еще одна проверка, за которой следует правильно спрогнозированная ветвь, а не проверка и меньшее загрязнение таблиц прогнозирования ветвлений.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...