Introduction
The C++ type system is a powerful tool for catching bugs before they reach production, but raw built-in types (int, double, std::string) carry no semantic meaning. A function void transfer(int from_account, int to_account, double amount) compiles just fine when you accidentally swap the account IDs — because both are just int.
Strong typing solves this by wrapping primitive types in distinct, non-interchangeable wrapper types. If AccountId and Amount are different types, the compiler rejects the swapped-argument mistake at compile time — a category of bug that is impossible to introduce.
This article compares three modern C++ libraries that make strong typing practical and ergonomic: type_safe by Jonathan Müller (foonathan), NamedType by Jonathan Boccara, and strong_type by Björn Fahller (rollbear). Each takes a different approach to the same fundamental problem.
| Feature | type_safe | NamedType | strong_type |
|---|---|---|---|
| Stars | 1,641 | 825 | 481 |
| Approach | Zero-overhead wrappers | CRTP mixin-based | Additive composition |
| Arithmetic support | Yes (checked) | Via skill mixins | Via modifiers |
| Comparison operators | Full | Opt-in | Automatic |
| Streaming («) | Yes | Opt-in | Opt-in |
| C++ standard | C++11/14 | C++14 | C++14/17/20 |
| Header-only | Yes | Yes | Yes |
| Integrates with existing code | Explicit conversions | Implicit via get() | Explicit .value() |
| Serialization support | Manual | Via skill | Manual |
| Documentation quality | Reference + blog | Extensive blog series | Header + examples |
type_safe: The Zero-Overhead Safety Net
type_safe (foonathan/type_safe) by Jonathan Müller is the most comprehensive type-safety library in the C++ ecosystem. At 1,641 stars, it’s widely adopted in safety-critical and embedded systems where preventing bugs at compile time is paramount.
type_safe goes beyond simple strong typedefs and provides safe alternatives to common C++ primitives: ts::integer<T> (checked arithmetic), ts::floating_point<T> (no NaN/Inf), ts::boolean (no implicit int conversion), and ts::constrained_type (runtime value validation).
CMake Integration
| |
Strong Typedefs with Arithmetic
| |
The ts::strong_typedef_op template parameters control which operators are generated — addition, subtraction, comparison, hashing, I/O — giving fine-grained control over what operations are permitted on each domain type.
Checked Integer Arithmetic
| |
type_safe’s ts::integer<T> wraps any integer type with checked arithmetic — additions, subtractions, and multiplications that would overflow throw exceptions rather than silently wrapping around. This is invaluable for financial calculations, embedded systems, and any domain where integer overflow could have serious consequences.
NamedType: The Fluent API Approach
NamedType (joboccara/NamedType) by Jonathan Boccara takes a different philosophical approach. Instead of providing a rigid set of safety wrappers, it uses CRTP (Curiously Recurring Template Pattern) with composable “skills” — mixins that add specific capabilities to your strong type.
Installation
| |
Composable Skills Pattern
| |
The skill-based approach is particularly elegant for domain modeling — you define exactly which operations make sense for each type, and the compiler enforces them. A UserId might be Comparable and Printable but not Addable, while a Distance would be Addable and Multiplicable.
strong_type: Additive Composition for Modern C++
strong_type (rollbear/strong_type) by Björn Fahller represents the most modern approach, leveraging C++17 and C++20 features for maximum ergonomics. Rather than CRTP mixins, it uses an additive composition model where you specify which properties (modifiers) your type should have.
CMake FetchContent
| |
Modern Composition API
| |
strong_type’s additive model is particularly clean — you can see at a glance exactly what operations a type supports by reading its template parameter list. The library also supports difference types (subtracting two Temperature values produces a Temperature::difference), which is useful for dimensional analysis.
Real-World Integration Pattern
Here’s a practical example combining strong types with a REST API handler, where type safety prevents common API parameter bugs:
| |
For developers working with physical quantities, strong types pair naturally with our C++ Units of Measurement Libraries guide — dimensional analysis libraries use the same strong typing principles to prevent mixing meters with seconds. If you’re building APIs that benefit from type-level validation, our C++ Enum Reflection Libraries overview covers complementary techniques for making enum types safer and more expressive. For a different angle on type safety, see our C++ Template Linear Algebra Libraries comparison where matrix dimensions are enforced at compile time.
FAQ
Do strong types incur runtime overhead?
No. All three libraries are designed for zero runtime overhead — the type wrappers are templates that the compiler optimizes away entirely. A strong::type<double, Tag> has the same memory layout and assembly as a plain double. The type checking happens at compile time; at runtime, the code is identical to using raw types. This is confirmed by compiler explorer: the generated assembly for operations on strong types is bit-for-bit identical to operations on the underlying type at any optimization level -O1 or above.
How do these libraries compare to std::chrono?
std::chrono::duration is actually a strong type built into the standard library — it prevents adding seconds to meters and converts between units automatically. The libraries in this comparison generalize that concept to ANY domain type. If you’ve used std::chrono and appreciated never accidentally mixing seconds with milliseconds, you already understand the value proposition of strong typing. type_safe, NamedType, and strong_type extend this safety to account IDs, temperatures, distances, voltages, and every other numeric value in your codebase.
Can I serialize strong types to JSON/Protobuf?
Yes, but you need to handle conversion explicitly. The safest pattern is to extract the underlying value for serialization and reconstruct the strong type on deserialization:
| |
For Protobuf, store the raw value in the message and wrap it in the strong type when constructing domain objects. This is actually a feature — the explicit boundary between serialization format and domain types is where bugs are most likely to be caught.
Which library should I choose for a new C++20 project?
For C++20, strong_type offers the cleanest API with its additive composition model. The syntax strong::type<double, Tag, strong::equality, strong::arithmetic> makes it immediately clear what operations a type supports, and the C++20 concepts integration provides excellent compiler error messages. For projects requiring C++14/17 compatibility, NamedType’s skill-based approach is equally capable. If you need checked arithmetic (overflow detection) in addition to strong typing, type_safe is the best choice — it includes ts::integer<T> with built-in overflow checking that the other two libraries don’t provide.
How do strong types interact with template code and generic algorithms?
Strong types work well with template code, but you may need to be explicit about conversions:
| |
The libraries provide get() or value() methods or static_cast for accessing the underlying value when interfacing with generic code that expects raw numeric types. Note that std::accumulate needs the explicit conversion in the lambda — this is intentional: the library forces you to be explicit at the boundary between strongly-typed domain code and generic numeric algorithms.
💰 想测试你的市场判断力?我用 Polymarket 做预测市场交易——这是全球最大的预测市场平台,从大选结果到技术监管时间线,什么都可以押注。和赌博不同,这是真正的信息市场:你懂的信息越多,胜率越高。我靠预测技术相关事件的走向已经赚了不少。用我的邀请链接注册:Polymarket.com