Walks Alexis King’s parse-don’t-validate paradigm through a date-parsing example in C++98, C++11, C++17, and C++23, using private constructors and static factories to enforce valid state at construction.
Key Takeaways
Core idea: use the type system so a successfully constructed Birthdate is guaranteed valid, eliminating downstream validation branches.
C++98 version uses enum ParseStatus, private constructor, and manual digit parsing for embedded/no-heap/no-exception environments.
C++17 version uses std::optional<Birthdate> with a private constructor and static parse() factory, avoiding exception-based control flow across library boundaries.
C++23 adds std::expected for rich error information without exceptions, building on the same private-ctor pattern.
The author treats the parse boundary as a hard wall: code consuming Birthdate should never need to re-validate its fields.
Hacker News Comment Review
Commenters identified the C++11 example as the weakest: its public throwing constructor accepts Birthdate(0, 2, 30) cleanly because it skips year and leap-year checks, undermining the article’s own thesis.
There is debate about whether the epoch() static factory adds anything over a plain default constructor; the private-ctor plus static-factory shape in C++17/23 is where commenters see the real mechanical insight.
One commenter pushed back that C is already capable of this pattern via pointer-or-null returns, sentinel values, or out-parameter error codes, questioning the C++ framing as uniquely enabling.
Notable Comments
@_alphageek: flags C++11 public throwing constructor as the article’s self-contradiction; private ctor plus static factory is the actual insight from King’s essay.
@bregma: “Author has used LLMs to generate Java code in C++. It detracts from his point.”
@actionfromafar: raises that parse-don’t-validate feels less distinct in functional style, where parsing and validating naturally blur.