47 Intermediate The Hundred
The Search Miss That Threw
the missing colon it shrugs off, then dies on
#include <iostream>
#include <string>
std::string value_of(const std::string& line) {
return line.substr(line.find(':') + 1); // everything after the ':'
}
std::string from_colon(const std::string& line) {
return line.substr(line.find(':')); // the ':' and everything after it
}
int main() {
std::cout << "1 [" << value_of("timeout:30") << "]" << std::endl;
std::cout << "2 [" << value_of("verbose") << "]" << std::endl;
std::cout << "3 [" << from_colon("verbose") << "]" << std::endl;
}
Run it. "verbose" has no colon — what do lines 2 and 3 print?
Answer
Line 2 prints 2 [verbose] — the whole line, cheerfully handed back as its own value.
Line 3 prints nothing at all: the program dies with an uncaught std::out_of_range.
Why¶
find reports "no match" by returning std::string::npos, which is size_t(-1) —
18,446,744,073,709,551,615 on x86-64, the same sentinel that fools >= 0 in entry 25.
Hand it to substr as a starting position and the standard mandates a throw, because
pos > size():
terminate called after throwing an instance of 'std::out_of_range'
what(): basic_string::substr: __pos (which is 18446744073709551615) > this->size() (which is 7)
The reflexive + 1 is arguably worse. Unsigned arithmetic wraps, so npos + 1 is exactly
0, and substr(0) is a perfectly legal request for the entire string — no exception, no
crash, just a wrong answer that flows quietly downstream. (pos == size() is legal too, so
"timeout:" correctly yields an empty value; only pos > size() throws.) g++ 11.5 is no
help: -Wall -Wextra, and even -Wconversion -Wsign-conversion, compile this silently —
every value involved is a valid size_t.
The fix¶
Test the sentinel before you slice:
std::string value_of(const std::string& line) {
std::size_t colon = line.find(':');
if (colon == std::string::npos) return {}; // no ':' — no value
return line.substr(colon + 1);
}
Takeaway: find signals failure with a perfectly valid-looking number — compare it to
npos before it ever reaches substr.
Try it: g++ -std=c++17 main.cpp -o demo && ./demo