1. Problem It Solves
Some extension or metadata boundaries must carry a copyable value whose type is not known to the receiving container. std::any erases that concrete type while preserving safe runtime type checking.
This lesson reduces that broad problem to one fixed-input program so the language rule and its observable result can be checked independently.
2. Prerequisites
A C++17 compiler invoked with warnings enabled and the earlier lessons listed in the course order.
Know static types, runtime type information, copying, exceptions, and pointer-style checked access.
3. Core Idea
An any owns one copyable object or is empty. std::any_cast<T> retrieves the exact stored type; value/reference forms throw on mismatch, while pointer forms return null.
Keep the type, object lifetime, ownership, and evaluation boundary visible while reading the example; syntax is useful only when those semantics are understood.
4. Minimal Syntax
std::any value = std::string{"C++17"};
if (const auto* text = std::any_cast<std::string>(&value)) {
use(*text);
}5. How It Works
An any first stores an integer, then is assigned an owned string object.
Pointer casts test the runtime type without exceptions, and the program prints only after each exact match.
The program prints
integer: 42,text: C++17, and a false integer check, giving a small test oracle that can be compared with the prediction made before compilation.
6. Common Mistakes
Frequent casts and undocumented stored types move errors from compilation to runtime; a variant is better when the alternative set is closed.
A successful build is not proof of correct semantics. Recheck lifetimes, invalidation, ordering, error paths, and required headers or link flags for the real program.
7. When to Use It
Use this technique when an open-ended boundary must own heterogeneous copyable values and runtime discovery is intentional.
Choose a simpler C++11/14 form when the C++17 rule does not improve safety, clarity, or measured performance for the supported toolchains.
8. Simple Example
The sample changes the erased type and uses non-throwing pointer casts, making successful and failed type tests explicit.
The companion .cpp file has no input or external dependency. Predict the complete output, compile it, run it, then change one constant and explain the new result.
Complete sample code
Source file
cpp17/27_any_type_erasure/main.cpp
#include <any>
#include <iostream>
#include <string>
int main() {
std::any value = 42;
if (const auto* number = std::any_cast<int>(&value)) {
std::cout << "integer: " << *number << '\n';
}
value = std::string{"C++17"};
if (const auto* text = std::any_cast<std::string>(&value)) {
std::cout << "text: " << *text << '\n';
}
std::cout << "is integer: "
<< (std::any_cast<int>(&value) != nullptr) << '\n';
}
9. Key Takeaways
Type erasure buys interface flexibility by giving up compile-time knowledge; keep the storage protocol small and documented.
C++17 mode must be selected explicitly; a newer compiler default can otherwise hide a portability error.
Warnings, deterministic examples, and small assertions turn a remembered rule into evidence.
Document any lifetime, ownership, synchronization, or allocation contract at the API boundary.
10. Self-Check Questions
Easy — What problem does std::any and Type Erasure address?
Medium — What does the pointer form of
any_cast<int>return after a string is stored?Hard — Why can
std::anynot directly store an ordinary move-only value in C++17?