Skip to content

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

Open in Compiler Explorer ↗ Quiz this entry