Building a vector from two positions
C++ has no slice syntax like v[1:4]. Instead, you build a new vector from two positions: vector<string> part(v.begin() + i, v.begin() + j);. The new vector gets a copy of the items from index i up to, but not including, index j.
vector<string> letters = {"a", "b", "c", "d", "e"};
vector<string> middle(letters.begin() + 1, letters.begin() + 4);
// middle is {"b", "c", "d"}
// letters is still {"a", "b", "c", "d", "e"}
The items at indexes 1, 2 and 3 are copied. Index 4 is where the copy stops, so "e" is left out. The number of items copied is always j - i: here, 4 - 1 = 3.
Going to the end
For "everything from index i on", use v.end() as the second position. It sits just past the last item.
vector<string> tail(letters.begin() + 3, letters.end());
// tail is {"d", "e"}
The same construction can be written as an expression, for example vector<string>(v.begin(), v.begin() + 2), which is handy in a return statement. If the two positions are equal, the new vector is empty.
It is a copy
The new vector owns its own items. Changing middle[0] to "X" leaves letters[1] as "b", and the original vector keeps its size too.
Common mistakes
- Counting the second position as included.
begin() + 3as the end gives three items from the start, at indexes 0, 1 and 2. - Going past the end. C++ does not check the positions. The first position must not come after the second, and neither may go beyond
v.end(). Otherwise the program can crash. - Expecting the original to change. A slice never removes anything from the vector it came from. To remove items, use
erase.
The exercise: a race podium
runners holds Ana, Ben, Cleo, Dan, Eve in finishing order. The first three go on the podium, so podium is built from runners.begin() to runners.begin() + 3. Everyone else is in rest, built from runners.begin() + 3 to runners.end(). The two ranges meet at index 3, so nobody is missed or counted twice.
vector<string> runners = {"Ana", "Ben", "Cleo", "Dan", "Eve"};
vector<string> podium(runners.begin(), runners.begin() + 3);
vector<string> rest(runners.begin() + 3, runners.end());
cout << podium[0] << " " << podium[1] << " " << podium[2] << endl;
cout << rest[0] << " " << rest[1] << endl;
cout << runners.size() << endl;
The output is:
Ana Ben Cleo
Dan Eve
5
The last line shows that runners still has all five items.
Picking the numbers
Think of the positions as the gaps between items. The vector a b c d e has gaps at 0 (before a) up to 5 (after e). A slice from begin() + 1 to begin() + 4 takes everything between gap 1 and gap 4, which is b c d. Seen this way, the end is not included simply because the copy stops at that gap.
It also explains why begin() + n works for "the first n items", and why a slice from a position to itself is empty. You can slice by counting from the end with v.end() - k, for example the last two items with v.end() - 2 to v.end().
A slice of the whole vector, vector<string>(v.begin(), v.end()), is just a copy of it.
The slice has its own type, which must fit the items you copy: a vector<string> slice of a vector<string>, or a vector<int> slice of a vector<int>. Because it is a separate vector, its indexes start at 0 again: podium[0] is the first runner, and rest[0] is runners[3], the fourth one.
Key takeaways
vector<T> part(v.begin() + i, v.begin() + j)copies indexesitoj - 1.- Use
v.end()to copy to the end. - The result is a separate copy; the original is untouched.
- Keep both positions inside the vector.
