std::string owns a sequence of characters, and std::vector<T> owns a growable sequence of elements of type T. Both manage their storage automatically. Copying either normally creates an independent value; destroying it releases the storage it owns. These types are useful defaults because most programs need text and lists before they need specialized data structures.
Both provide size(), empty() and indexed access. A vector stores its elements contiguously, except for the specialized vector<bool>. A string stores contiguous char elements and provides a terminating zero after its contents. Its logical size excludes that terminator and may include embedded zero characters.
Read a small reading list
Save as collections.cpp:
#include <iostream>
#include <string>
#include <string_view>
#include <vector>
bool has_prefix(std::string_view text, std::string_view prefix) {
return text.starts_with(prefix);
}
int main() {
std::vector<std::string> titles;
titles.reserve(4);
std::string line;
while (std::getline(std::cin, line)) {
if (line.empty()) {
break;
}
titles.push_back(line);
}
if (std::cin.bad() || (std::cin.fail() && !std::cin.eof())) {
std::cerr << "Reading failed\n";
return 1;
}
std::cout << "Titles: " << titles.size() << '\n';
if (!titles.empty()) {
std::cout << "First: " << titles.at(0) << '\n';
}
for (const std::string& title : titles) {
if (has_prefix(title, "C++")) {
std::cout << "C++ pick: " << title << '\n';
}
}
}
clang++ -std=c++20 -Wall -Wextra -Wpedantic -g collections.cpp -o collections
printf 'C++ Basics\nPractical Testing\n\n' | ./collections
Expected output:
Titles: 2
First: C++ Basics
C++ pick: C++ Basics
getline preserves spaces inside each title and removes the line delimiter. An empty line ends entry; end-of-input is also a normal stopping condition. Each push_back(line) copies the current line into the vector, so reading the next line does not change saved titles. The range loop borrows each stored string through a const reference rather than copying it.
Size, capacity and access
Size is the number of live elements. Capacity is how many elements can fit in the currently allocated storage before growth requires reallocation. reserve(4) requests capacity for at least four titles; it does not create four elements. resize(4) would create or remove elements until the size became four.
The difference is observable during indexing. Accessing element zero immediately after reserve(4) is invalid because the vector is still empty. For a vector, operator[] requires a valid index; violating that requirement has undefined behavior in C++20. at() checks the index and throws std::out_of_range when it is invalid. front(), back() and pop_back() also require a nonempty vector.
Construction syntax matters: std::vector<int> values(3, 7) creates three sevens; std::vector<int> values{3, 7} creates two elements, three and seven. Braces select an initializer-list constructor when applicable, so they are not interchangeable with parentheses.
Views, encoding and invalidation
std::string_view borrows characters without owning them. Passing a view by value is convenient when a function only inspects text during the call. The example's has_prefix saves nothing, and every title remains alive throughout its call. Do not store a view into a temporary string or assume view.data() ends at a zero character; a view may describe only a substring.
A string's size counts char elements, not user-perceived characters. If those elements contain UTF-8, one visible character may occupy multiple code units. C++20's ordinary string operations do not validate UTF-8 or perform Unicode case folding. This lesson's prefix uses plain ASCII; production Unicode processing needs an explicit encoding policy.
When vector growth reallocates storage, all references, pointers and iterators to its elements become invalid. Without reallocation, appending preserves references to existing elements but invalidates the old past-the-end iterator. Erasing invalidates iterators and references at or after the erased position. Reserving a guessed capacity is not a lifetime guarantee if later growth exceeds it. Keep mutation and traversal in separate phases when possible.
Practice and solution guidance
- Ignore duplicate titles while preserving entry order. Guidance: search existing titles before appending; explain why repeated linear searches make the whole process quadratic in the worst case.
- Add a ten-title limit. Guidance: compare size with the limit before insertion. Capacity is an allocation detail, not the business rule.
- Return the first title from a helper that creates a local vector. Guidance: return an owning string by value; a reference or string view would outlive its owner.

