Найти положение элемента в цикле C ++ 11 на основе диапазона для цикла?

Предположим, у меня есть следующий код:

vector<int> list;
for(auto& elem:list) {
    int i = elem;
}

Могу ли я найти должностьelem в векторе без ведения отдельного итератора?

 ShadowRanger18 янв. 2018 г., 04:22
 Eitan T09 июн. 2012 г., 17:49
Это невозможно в контейнерах STL, если не используетсяstd::find или какая-то другая функция перебора. Вы не можете заключить итераторы из содержащихся элементов. Почему бы не поддерживать итератор?
 jrok09 июн. 2012 г., 17:42
Это не то, для чего основан диапазон (хе, это каламбур?)
 Fred Finkle09 июн. 2012 г., 21:25
По двум причинам. Во-первых, все, что я хочу сделать (в данном случае), это посмотреть, нахожусь ли я на последнем элементе или нет :), а во-вторых, компилятор должен поддерживать его, почему я не могу получить к нему доступ? & Quot; это & Quot; переменная с областью видимости поддерживается компилятором, почему бы и нет? Или предоставьте альтернативный (но все же удобный) синтаксис, который, как это делает javascript, устанавливает переменную, которая изменяется по мере прохождения цикла. для (авто и индекс: список)
 iFreilicht31 июл. 2014 г., 18:11
@FredFinkle вы на самом деле правы,there is an iterator, но при использовании диапазона на основеfor loop, это внутреннее имя компилятора и поэтому не может использоваться в вашем коде. Поэтому, если вы действительно хотите знать, находитесь ли вы на последнем элементе, вам следует использоватьfor(;;) петля.

Ответы на вопрос(10)

которое, вероятно, превосходит большинство других по простоте, времени компиляции и качеству генерации кода:

#include <iostream>

#define fori(i, ...) if(size_t i = -1) for(__VA_ARGS__) if(i++, true)

int main() {
    fori(i, auto const & x : {"hello", "world", "!"}) {
        std::cout << i << " " << x << std::endl;
    }
}

Результат:

$ g++ -o enumerate enumerate.cpp -std=c++11 && ./enumerate 
0 hello
1 world
2 !

Хитрость в том, чтобы использовать композицию: вместо того, чтобы перебирать контейнер напрямую, вы & quot; zip & quot; это с индексом по пути.

Специализированный код на молнии:

template <typename T>
struct iterator_extractor { typedef typename T::iterator type; };

template <typename T>
struct iterator_extractor<T const> { typedef typename T::const_iterator type; };


template <typename T>
class Indexer {
public:
    class iterator {
        typedef typename iterator_extractor<T>::type inner_iterator;

        typedef typename std::iterator_traits<inner_iterator>::reference inner_reference;
    public:
        typedef std::pair<size_t, inner_reference> reference;

        iterator(inner_iterator it): _pos(0), _it(it) {}

        reference operator*() const { return reference(_pos, *_it); }

        iterator& operator++() { ++_pos; ++_it; return *this; }
        iterator operator++(int) { iterator tmp(*this); ++*this; return tmp; }

        bool operator==(iterator const& it) const { return _it == it._it; }
        bool operator!=(iterator const& it) const { return !(*this == it); }

    private:
        size_t _pos;
        inner_iterator _it;
    };

    Indexer(T& t): _container(t) {}

    iterator begin() const { return iterator(_container.begin()); }
    iterator end() const { return iterator(_container.end()); }

private:
    T& _container;
}; // class Indexer

template <typename T>
Indexer<T> index(T& t) { return Indexer<T>(t); }

И используя это:

#include <iostream>
#include <iterator>
#include <limits>
#include <vector>

// Zipper code here

int main() {
    std::vector<int> v{1, 2, 3, 4, 5, 6, 7, 8, 9};

    for (auto p: index(v)) {
        std::cout << p.first << ": " << p.second << "\n";
    }
}

Вы можете увидеть это наideoneхотя в нем отсутствует поддержка цикла for-range, поэтому он менее симпатичен.

EDIT:

Просто вспомнил, что я должен чаще проверять Boost.Range. к сожалению нетzip диапазон, но я нашел Perl:boost::adaptors::indexed, Однако для получения индекса требуется доступ к итератору. Позор: х

В противном случае сcounting_range и общийzip Я уверен, что можно было бы сделать что-то интересное ...

В идеальном мире я бы представил:

int main() {
    std::vector<int> v{1, 2, 3, 4, 5, 6, 7, 8, 9};

    for (auto tuple: zip(iota(0), v)) {
        std::cout << tuple.at<0>() << ": " << tuple.at<1>() << "\n";
    }
}

Сzip автоматическое создание представления в виде набора кортежей ссылок иiota(0) просто создаем & quot; ложь & quot; диапазон, который начинается с0 и просто считается до бесконечности (или, ну, максимум его типа ...).

 09 июн. 2012 г., 21:58
Как насчетcounting_range (или жеboost::counting_iterator) + boost::zip_iterator?
 04 июл. 2012 г., 14:57
@Xeo: я согласен :)
 10 июн. 2012 г., 12:00
@ildjarn: Да, у Boost.Iterators есть строительные блоки (кажется), однако нет соответствующего диапазона, что раздражает.
 Fred Finkle09 июн. 2012 г., 21:30
Это дает массу полезной информации. Благодарю. Я поиграюсь с кодом. Как я уже упоминал выше, «индекс» Код - это то, что я хотел бы, чтобы язык предоставил.
 04 июл. 2012 г., 14:39
Обратите внимание, что вы могли бы изменить свойIndexer также правильно принимать и хранить аргументы rvalue, изменяя тип_container к типу значения, если исходный аргумент является rvalue иstd::move/std::forward аргумент в.

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

for(auto& elem:list) {
//  loop code ...
    if(&elem == &*std::begin(list)){ ... special code for first element ... }
    if(&elem == &*std::prev(std::end(list))){ ... special code for last element ... }
//  if(&elem == &*std::rbegin(list)){... (C++14 only) special code for last element ...}
//  loop code ... 
}

EDIT: Например, это печатает контейнер, пропуская разделитель в последнем элементе. Работает для большинства контейнеров, которые я могу себе представить (включая массивы), (онлайн-демонстрацияhttp://coliru.stacked-crooked.com/a/9bdce059abd87f91):

#include <iostream>
#include <vector>
#include <list>
#include <set>
using namespace std;

template<class Container>
void print(Container const& c){
  for(auto& x:c){
    std::cout << x; 
    if(&x != &*std::prev(std::end(c))) std::cout << ", "; // special code for last element
  }
  std::cout << std::endl;
}

int main() {
  std::vector<double> v{1.,2.,3.};
  print(v); // prints 1,2,3
  std::list<double> l{1.,2.,3.};
  print(l); // prints 1,2,3
  std::initializer_list<double> i{1.,2.,3.};
  print(i); // prints 1,2,3
  std::set<double> s{1.,2.,3.};
  print(s); // print 1,2,3
  double a[3] = {1.,2.,3.}; // works for C-arrays as well
  print(a); // print 1,2,3
}
 04 авг. 2014 г., 11:00
Обратите внимание (до необоснованного понижения голосов), что автор вопроса задает этот вопрос в контексте обнаружения последнего элемента в цикле for-ranged для контейнера. Для этого я не вижу причин, почему сравнивать&elem а также&*std::prev(std::end(list)) не будет работать или быть практичным. Я согласен с другим ответом, что для этого больше подходит итератор для, но все же.
 08 нояб. 2014 г., 21:09
@MarcGlisse,int i код был просто примером. Я уберу это, чтобы избежать путаницы. Даже если вы используетеsize перед циклом вам понадобится счетчик.
 08 нояб. 2014 г., 21:03
Кажется, легче объявитьint i=c.size(); перед циклом и тестомif(--i==0).

вы не можете (по крайней мере, не без усилий). Если вам нужно положение элемента, вы не должны использовать диапазон для. Помните, что это просто удобный инструмент для наиболее распространенного случая: выполнить некоторый код для каждого элемента. В менее распространенных обстоятельствах, когда вам нужно положение элемента, вы должны использовать менее удобный регулярныйfor петля.

вы можете сделать это в функциональном стиле:

#include <iostream>
#include <string>
#include <vector>
#include <functional>

template<typename T>
void for_enum(T& container, std::function<void(int, typename T::value_type&)> op)
{
    int idx = 0;
    for(auto& value : container)
        op(idx++, value);
}

int main()
{
    std::vector<std::string> sv {"hi", "there"};
    for_enum(sv, [](auto i, auto v) {
        std::cout << i << " " << v << std::endl;
    });
}

Работает с clang 3.4 и gcc 4.9 (не с 4.8); для обоих нужно установить-std=c++1y, Причина, по которой вам нужен c ++ 14, заключается вauto параметры в лямбда-функции.

 29 нояб. 2018 г., 16:29
std::function использует стирание типа, которое стоит дорого. Почему бы не использоватьtemplate<typename T, typename Callable> void for_enum(T& container, Callable op) значит, вам не нужно платить за удаление типа?

vector<int> list;
for(auto& elem:list) {
    int i = (&elem-&*(list.begin()));
}

гдеi будет вашим обязательным индексом.

Это использует тот факт, чтоВекторы С ++ всегда смежны.

Python для MIT (хотя C ++ 17):

GitHub

Сообщение блога

Действительно приятно использовать:

std::vector<int> my_vector {1,3,3,7};

for(auto [i, my_element] : en::enumerate(my_vector))
{
    // do stuff
}

существует очень элегантное решение с использованием упомянутыхповышение :: адаптеры :: индексируется:

std::vector<std::string> strings{10, "Hello"};
int main(){
    strings[5] = "World";
    for(auto const& el: strings| boost::adaptors::indexed(0))
      std::cout << el.index() << ": " << el.value() << std::endl;
}

Можешь попробовать

Это работает почти так же, как «идеальное мировое решение». упоминается, имеет красивый синтаксис и лаконичен. Обратите внимание, что типel в этом случае что-то вродеboost::foobar<const std::string&, int>, поэтому он обрабатывает ссылку там, и копирование не выполняется. Это даже невероятно эффективно:https://godbolt.org/g/e4LMnJ (Код эквивалентен хранению собственной переменной-счетчика, которая так же хороша, как и получается)

Для полноты альтернативы:

size_t i = 0;
for(auto const& el: strings) {
  std::cout << i << ": " << el << std::endl;
  ++i;
}

Или используя непрерывное свойство вектора:

for(auto const& el: strings) {
  size_t i = &el - &strings.front();
  std::cout << i << ": " << el << std::endl;
}

Первый генерирует тот же код, что и версия повышающего адаптера (оптимально), а последний на 1 инструкцию длиннее:https://godbolt.org/g/nEG8f9

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

for(auto const& el: strings) {
  bool isLast = &el == &strings.back();
  std::cout << isLast << ": " << el << std::endl;
}

Это работает для каждого стандартного контейнера, ноauto&/auto const& должен использоваться (так же, как указано выше), но это рекомендуется в любом случае. В зависимости от входных данных это также может быть довольно быстро (особенно, когда компилятор знает размер вашего вектора)

Заменить&foo отstd::addressof(foo) быть в безопасности для общего кода.

 20 июл. 2018 г., 10:52
Это действительно элегантно!
 26 янв. 2019 г., 22:53
Теперь, если только это не зависит от повышения ....
 20 июл. 2018 г., 14:16
Я добавил 2 варианта с помощью сравнения сгенерированного кода для полной полноты, а также обратился к необходимости ОП (в комментариях) для обнаружения последнего элемента.

ения индекса, поддерживать индекс довольно просто, как показано ниже. Я не думаю, что есть более чистое / простое решение для диапазона, основанного на петлях. Но на самом деле, почему бы не использовать стандарт для (;;)? Это, вероятно, сделает ваши намерения и код яснее.

vector<int> list;
int idx = 0;
for(auto& elem:list) {
    int i = elem;
    //TODO whatever made you want the idx
    ++idx;
}
 11 дек. 2016 г., 21:19
(idx означает «поддержание отдельного итератора»)

Тем не менее, в вашем случае это можно вычислить с помощью арифметики указателя, так какvector хранит свои элементы непрерывно (*)

vector<int> list;
for(auto& elem:list) { 
    int i = elem;
    int pos = &elem-&list[0]; // pos contains the position in the vector 

    // also a &-operator overload proof alternative (thanks to ildjarn) :
    // int pos = addressof(elem)-addressof(list[0]); 

}

Но это явно плохая практика, поскольку она запутывает код & amp; делает его более хрупким (он легко ломается, если кто-то меняет тип контейнера, перегружает& оператор или заменить "авто" и " "авто". удачи в отладке этого!)

ПРИМЕЧАНИЕ. Смежность гарантирована для вектора в C ++ 03, а также для массива и строки в стандарте C ++ 11.

 Fred Finkle09 июн. 2012 г., 21:27
Не знал, что смежность гарантирована. Не хотел бы использовать это здесь, но полезно знать.
 09 июн. 2012 г., 20:28
& Quot;it easily breaks if someone ... overload the & operator& Quot; Вот чтоstd::addressof для. : -]
 18 июл. 2013 г., 02:17
Почему бы не использовать std :: distance для определения позиции?
 09 июн. 2012 г., 18:02
Да, это указано в стандарте. Смежность гарантирована дляvector в C ++ 03 иarray а такжеstring в C ++ 11.
 09 июн. 2012 г., 21:10
Вы правы. Таким образом, версия, защищенная от & amp; перегрузки, будет выглядеть следующим образом: int pos = addressof (elem) - addressof (list [0]); .... Оболочка итератора Мэтью М. намного лучше :)

Ваш ответ на вопрос