Предположим, у меня есть следующий код:
vector<int> list;
for(auto& elem:list) {
    int i = elem;
}
Могу ли я найти положение elem в векторе, не поддерживая отдельный итератор?
Предположим, у меня есть следующий код:
vector<int> list;
for(auto& elem:list) {
    int i = elem;
}
Могу ли я найти положение elem в векторе, не поддерживая отдельный итератор?
Да, вы можете, просто возьмите немного массажа;)
Трюк состоит в том, чтобы использовать композицию: вместо того, чтобы перебирать контейнер непосредственно, вы "застегиваете" его индексом по пути.
Специализированный код на молнии:
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. Однако для доступа к итератору требуется доступ к индексу. Позор: x
В противном случае с 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) просто создавая "ложный" диапазон, который начинается с 0 и просто отсчитывается до бесконечности (или, ну, максимум его типа...).
jrok прав: диапазоны для циклов не предназначены для этой цели.
Однако в вашем случае его можно вычислить с помощью арифметики указателя, так как 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]); 
}
Но это, очевидно, плохая практика, так как она запутывает код и делает его более хрупким (он легко ломается, если кто-то меняет тип контейнера, перегружает оператор & или заменяет "auto &" на "auto". отлаживать это!)
ПРИМЕЧАНИЕ. Непрерывность гарантируется для вектора в С++ 03, а массив и строка в стандарте С++ 11.
Нет, вы не можете (по крайней мере, не без усилий). Если вам нужна позиция элемента, вы не должны использовать диапазон. Помните, что это просто удобный инструмент для наиболее распространенного случая: выполните код для каждого элемента. В менее распространенных случаях, когда вам нужно положение элемента, вы должны использовать менее удобный обычный цикл for.
Если у вас есть компилятор с поддержкой С++ 14, вы можете сделать это в функциональном стиле:
#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. Причина, по которой вам нужен С++ 14, - это параметры auto в лямбда-функции.
Основываясь на ответе @Matthieu, есть очень элегантное решение, использующее упомянутый выше boost :: adapters :: indexed:
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) чтобы быть в безопасности для универсального кода.
Если вы настаиваете на использовании диапазона, основанного на индексе, и знаете индекс, довольно просто поддерживать индекс, как показано ниже. Я не думаю, что есть более чистое/более простое решение для диапазонов, основанных на циклах. Но почему бы не использовать стандарт для (;;)? Вероятно, это сделает ваше намерение и код самым ясным.
vector<int> list;
int idx = 0;
for(auto& elem:list) {
    int i = elem;
    //TODO whatever made you want the idx
    ++idx;
}
		Существует удивительно простой способ сделать это
vector<int> list;
for(auto& elem:list) {
    int i = (&elem-&*(list.begin()));
}
 где i будет вашим обязательным индексом.
Это использует тот факт, что векторы С++ всегда смежны.
Я прочитал из ваших комментариев, что одной из причин, по которой вы хотите знать индекс, является знать, является ли этот элемент первым/последним в последовательности. Если это так, вы можете сделать
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
}
		Вот решение на основе макросов, которое, вероятно, превосходит большинство других по простоте, времени компиляции и качеству генерации кода:
#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 !
		Тобиас Видлунд написал хороший перечисляемый заголовок только в стиле Python для MIT (хотя С++ 17):
Действительно приятно использовать:
std::vector<int> my_vector {1,3,3,7};
for(auto [i, my_element] : en::enumerate(my_vector))
{
    // do stuff
}
		Если вы хотите избежать необходимости писать вспомогательную функцию, имея переменная индекса, локальная для цикла, вы можете использовать лямбду с изменяемой переменной.:
int main() {
    std::vector<char> values = {'a', 'b', 'c'};
    std::for_each(begin(values), end(values), [i = size_t{}] (auto x) mutable {
        std::cout << i << ' ' << x << '\n';
        ++i;
    });
}