C ++ STL Вектори: Отримати ітератор з індексу?


200

Отже, я написав купу коду, який звертається до елементів у stl-векторі за індексом [], але тепер мені потрібно скопіювати лише фрагмент вектора. Схоже, vector.insert(pos, first, last)це функція, яку я хочу ... за винятком того, що я є лише першою та останньою як ints. Чи є приємний спосіб отримати ітератор цих значень?


1
Дивіться також: stackoverflow.com/q/2152986/365102
— Mateen Ulhaq

Якщо я не помиляюся, жодна з відповідей не перевіряє меж, що може бути проблемою. Зокрема, документи std :: advance говорять, що поведінка не визначена, якщо ви використовуєте її для проходження меж базових контейнерів.
— Мартін Печка

Відповіді:


293

Спробуйте це:

vector<Type>::iterator nth = v.begin() + index;

4
Як правило, ви можете використовувати ту саму арифметику з ітераторами STL, ніж з покажчиками. Вони призначені для обміну при використанні алгоритмів STL.
— Вінсент Роберт

18
@VincentRobert: Інакше. Покажчики є дійсними реалізаціями випадкових ітераторів STL, найпотужнішої категорії. Але інші, менш потужні категорії, такі як ітератори вперед, не підтримують однакову арифметику.
— MSalters

6
Я хотів би, щоб я додав свої п’ять центів до цієї відповіді та рекомендуюstd::next(v.begin(), index)
— стрику

87

спосіб, згаданий @dirkgently ( v.begin() + index )приємний і швидкий для векторів

але std::advance( v.begin(), index ) найбільш загальний спосіб і для ітераторів випадкового доступу також працює постійний час.

EDIT
відмінності у використанні:

std::vector<>::iterator it = ( v.begin() + index );

або

std::vector<>::iterator it = v.begin();
std::advance( it, index );

додано після приміток @litb.


не std ::перед вимагає ітератора non-const як першого аргументу?
— золотоПсевдо

ви можете використовувати std :: advance з const та non-const ітераторами
— bayda

1
ви не повинні довіряти msvc з цього приводу. він має нестандартне розширення, що змушує його приймати подібні речі, проте всі інші компілятори поводяться стандартно і відмовляються від цього.
— Йоханнес Шауб - ліб

1
Я думаю, що проблема полягає в плутанині щодо значення "const" :перед () буде радісно працювати над const_iterator <T>, який є мутаційним ітератором, що посилається на елемент const типу T; він не буде працювати над об'єктом ітератора, який є самим const (тобто "const iterator <T>" або "iterator <T> const").
— j_random_hacker

7
Якщо ви знаєте, що маєте справу з компанією std::vector, використовувати це не має сенсу std::advance. Це лише заманює вас думати, що ви пишете контейнерно-агностичний код (чого ви цього не робите, придумуючи правила недійсності ітератора, різні складності виконання та інше). Єдиний випадок, коли std::advanceє сенс, коли ви пишете шаблон самостійно, який не знає, з яким ітератором він має справу.
— Фріріх Раабе

47

Також; auto it = std::next(v.begin(), index);

Оновлення: потрібен компілятор, сумісний з C ++ 11x


2
Слід зазначити, що це спосіб C ++ 11! std :: next еквівалентно std ::перед. Використання цих функцій, а не використання арифметики, значно полегшує заміну типів контейнерів. Навіть працює на c-масивах afaik, як і std :: begin та std :: end.
— Zoomulator

2
Слід також зазначити, що std :: advance розроблений ідіот, оскільки він використовує посилання як вихід, а не повернене значення.
— Віктор Шер

1
for (auto it = початок (c); it! = end (c); advance (it, n)) {...}
— Zoomulator

2
Обидва мають своє використання. stda :: аванс корисний для зміни ітератора на місці. Це питання продуктивності в циклі. Я б вважав за краще наступне у випадку доручення, як ви пропонуєте. Я просто виявив це трохи різко, стверджуючи, що це ідіот. Обидві функції були розроблені з урахуванням різних ситуацій, хоча вони в основному однакові.
— Zoomulator

5
@Zoomulator: Якщо копіювання вашого ітератора викликає ефективність, у вас є більші проблеми.
— Mooing Duck


-3

Актуально std :: вектор повинні використовуватися як вкладка C, коли це необхідно. (C ++ стандартні запити про те, що для реалізації вектора, наскільки я знаю - заміна масиву у Вікіпедії ) Наприклад, цілком легально робити це наступне, на мою думку:

int main()
{

void foo(const char *);

sdt::vector<char> vec;
vec.push_back('h');
vec.push_back('e');
vec.push_back('l');
vec.push_back('l');
vec.push_back('o');
vec.push_back('/0');

foo(&vec[0]);
}

Звичайно, або foo не повинен копіювати адресу, передану як параметр, і зберігати її кудись, або ви повинні забезпечити у своїй програмі ніколи не натискати жодного нового елемента в vec, або просити змінити його ємність. Або ризик помилки сегментації ...

Тому у вашому прикладі це призводить до

vector.insert(pos, &vec[first_index], &vec[last_index]);

Мене здивує, чому вони вирішили відмовитися від ітераторів, якщо вони просто вказівники ... вони по суті "приховують" ці можливості.
— mpen

За постійність? Так як це дозволить вам легко видалити векторний екземпляр для будь-якого іншого типу контейнера у вашому коді.
— Ів Баумес

4
& vec [i] дає вказівник, який не обов'язково сумісний з вектором <> :: ітератором. vec.begin () + я все ще має перевагу бути будь-яким ітератором, який ваша бібліотека визначає, включаючи, наприклад, перевірені ітератори в режимі налагодження. Отже, якщо вам не потрібен покажчик (наприклад, для вводу / виводу), завжди слід віддавати перевагу ітераторам.
— sellibitze

@KerrekSB З 23.3.6.1 у стандартному проекті c ++: "Елементи вектора зберігаються безперервно, це означає, що якщо v - вектор <T, Аллокатор>, де T - якийсь тип, відмінний від bool, то він підкоряється ідентичності & v [ n] == & v [0] + n для всіх 0 <= n <v.size () "
— yves Baumes

2
@yvesBaumes: це не має нічого спільного з векторними ітераторами. Однак правда, що голі вказівники також є ітераторами - вони просто не векторні ітератори.
— Керрек СБ
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.