Vocabulary types like std::string and std::function allow us to share a lingua franca for dealing with common programming concepts. In C++17, we have a rich set of vocabulary types for dealing with the algebraic data types: std::pair and std::tuple (product types), std::optional and std::variant (sum types), and std::any (the ultimate in sum types--it can store almost anything). However, don't get carried away and start using std::tuple and std::variant return types from every function! Named class types are still the most effective way to keep your code readable.
Use std::optional to signal the possible lack of a value, or to signal the "not-yet-ness" of a data member.
Use std::get_if<T>(&v) to query the type of a variant; use std::any_cast<T>(&a) to query the type of an any. Remember that the type you provide must be an exact match...