Two weeks ago I asked you for help: I wanted to build a wall of examples of std::optional . I’m very grateful that a lot of you responded and I could move forward with the plan!

You’re amazing!

Let’s dive in the examples my readers have sent me!

A Reminder

To remind, I asked for some real-life examples of std::optional . It’s exciting to see in how many ways you use this vocabulary type in your projects. There are many options and variations. In this post, I’ve put all of them in a single place.

Most of the code is as I got it from the authors, in some places I had to shorten it and extract only the core parts.

Giveaway

For this experiment, I also had 2 x 25$ Amazon.com Gift Card. I randomly selected two participants, and I’ve contacted them already :)

I wonder if they spend that enormous amount of money on some C++ book or a course :)

The Series

This article is part of my series about C++17 Library Utilities. Here’s the list of the other topics that I’ll cover:

Resources about C++17 STL:

The Examples

Constructing a Query to a Database

Wojciech Razik used optional to represent possible query parameters:

class Query { std :: optional <int> limit_ ; std :: optional < std :: string > name_ ; // ... more params public : Query & Limit ( int l ) { limit_ = l ; return * this ;} Query & Name ( std :: string s ) { name_ = std :: move ( s ); return * this ;} std :: optional <int> GetLimit () const { return limit_ ;} std :: optional < std :: string > GetName () const { return name_ ; } }; void Select ( const Query & q ) { // couts for demonstration only std :: cout << " -

" ; if ( q . GetLimit ()) { std :: cout << "Limit: " << q . GetLimit (). value () << "

" ; } if ( q . GetName ()) { std :: cout << "Name: " << q . GetName (). value () << "

" ; } } int main () { Select ( Query {}. Name ( "Some name" )); Select ( Query {}. Limit ( 3 )); // You can find objects with empty fields! Select ( Query {}. Limit ( 5 ). Name ( "" )); }

Play with the code @Coliru

I like the idea of chaining to build the final query object.

Conversion from a String to an Integer

In the following example, Martin Moene applied std::optional to a function that converts strings to integers.

auto to_int ( char const * const text ) -> std :: optional <int> { char * pos = nullptr ; const int value = std :: strtol ( text , & pos , 0 ); return pos == text ? std :: nullopt : std :: optional <int> ( value ); } int main ( int argc , char * argv [] ) { const char * text = argc > 1 ? argv [ 1 ] : "42" ; std :: optional <int> oi = to_int ( text ); if ( oi ) std :: cout << "'" << text << "' is " << * oi ; else std :: cout << "'" << text << "' isn't a number" ; }

Alternatively with more compact code:

if ( auto oi = to_int ( text )) std :: cout << "'" << text << "' is " << * oi ; else std :: cout << "'" << text << "' isn't a number" ;

Play with the code @Wandbox

Conversion from String, More Generic solution

jft went a bit further with the previous idea of string conversions and wrote a function that uses istringstream to convert to many different numeric types.

// Converts a text number to specified type. // All of the text must be a valid number of the specified type. // eg 63q is invalid // Defaults to type int // st - string to convert // returns either value of converted number or // no value if text number cannot be converted template < typename T = int > std :: optional < T > stonum ( const std :: string & st ) { const auto s = trim ( st ); bool ok = s . empty () ? false : ( std :: isdigit ( s . front ()) || ((( std :: is_signed < T >:: value && ( s . front () == '-' )) || ( s . front () == '+' )) && (( s . size () > 1 ) && std :: isdigit ( s [ 1 ])))); auto v = T {}; if ( ok ) { std :: istringstream ss ( s ); ss >> v ; ok = ( ss . peek () == EOF ); } return ok ? v : std :: optional < T > {}; } // use case: string snum = "42.5" ; if ( auto n = stonum <double> ( snum ); n . has_value ()) cout << snum << " is double " << * n << endl ; else cout << snum << " is not a double" << endl ;

Play with the code @Coliru

std::istream::operator>> has overloads for many numeric types, so with this one handy function you can potentially have a converter to many types from a string.

Monadic Extensions

This snippet comes from Lesley Lai

Full code @Gist

The basic idea is to be able to chain operations that return std::optional .

auto x = read_file ( "exist.txt" ) >> opt_stoi >> []( int n ) { return std :: make_optional ( n + 100 ); }; print ( x );

This is done by clever overloading of >> .

template < typename T1 , typename Func , typename Input_Type = typename T1 :: value_type , typename T2 = std :: invoke_result_t < Func , Input_Type > > constexpr T2 operator >>( T1 input , Func f ) { static_assert ( std :: is_invocable_v < decltype ( f ), Input_Type >, "The function passed in must take type" "(T1::value_type) as its argument" ); if (! input ) return std :: nullopt ; else return std :: invoke ( f , * input ); }

And the functions used in the example:

std :: optional < std :: string > read_file ( const char * filename ) { std :: ifstream file { filename }; if (! file . is_open ()) { return {}; } std :: string str (( std :: istreambuf_iterator <char> ( file )), std :: istreambuf_iterator <char> ()); return { str }; } std :: optional <int> opt_stoi ( std :: string s ) { try { return std :: stoi ( s ); } catch ( const std :: invalid_argument & e ) { return {}; } catch ( const std :: out_of_range & ) { return {}; } } template < typename T > constexpr void print ( std :: optional < T > val ) { if ( val ) { std :: cout << * val << '

' ; } else { std :: cerr << "Error

" ; } }

Play with the code @Coliru

And the notes from the author:

This snippet implement monadic bind operation that chain functions together without explicitly checking errors. The whole gist is inspired by Phil Nash’s talk at North Denver Metro C++ Meetup C++ meetup. I use optional here because it is in the standard library, Expected should fit the error handling job better since it stores information about why an error happened. I do not think the implementation of this function will change for Expected .

Geometry and Intersections

by Arnaud Brejeon

Full code @Gist

The original code is much longer and uses operator overloading, plus a separate type declaration Point and Line , but it should be clear what the code does:

std :: optional < Point > intersection ( const Line & a , const Line & b ) { const auto d1 = a . first - a . second ; const auto d2 = b . first - b . second ; const auto cross = d1 . x * d2 . y - d1 . y * d2 . x ; if ( std :: abs ( cross ) < 1e-6f ) { // No intersection return {}; } const auto x = b . first - a . first ; const auto t1 = ( x . x * d2 . y - x . y * d2 . x ) / cross ; return a . first + t1 * d1 ; }

Example use case:

const auto i0 = intersection ( Line ( Point (- 1 , 0 ), Point ( 1 , 0 )), Line ( Point ( 0 , - 1 ), Point ( 0 , 1 )) ); std :: cout << std :: boolalpha << i0 . has_value (); if ( i0 ) { std :: cout << " : " << i0 -> x << ", " << i0 -> y ; }

Simple optional chaining

by Jeremiah O’Neil

While we can chain optional in many ways, Jeremiah showed a simple way:

int a = //value one; int b = //value two; if ( optional <int> tmp , x ; ( tmp = fa ( a )) && ( x = fb ( b )) && ( x = fcd (* tmp , * x )) && ( x = fe (* x ))) { return * x ; } else { return 0 ; }

Each of the functions fa , fb , fcd , fe (what awesome names!) returns std::optional . But thanks to the short circuit rules and the evaluation happening from left to right the functions won’t be executed if the previous one fails (when a function returns nullopt .

Play with the code @Coliru

Handling a throwing constructor

Edoardo Morandi managed to wrap a throwing constructor into a wrapper class that instead of throwing allows you to check if the object is initialised or not.

Full code @Compiler Explorer

// A simple struct, without anything special related to exception handling struct S_impl { S_impl () = default ; // This can throw! S_impl ( std :: size_t s ) : v ( s ) {} std :: vector <double> & get () { return v ; } private : std :: vector <double> v ; }; // A (too) simple user interface for S_impl struct S : std :: optional < S_impl > { template < typename ... Args > // A `noexcept` wrapper to construct the real implementation. S ( Args &&... args ) noexcept : optional < S_impl >( // Construct std::optional inplace using constructor initialization, // leading to pre-C++20 ugly code to universal forwarding :( [ args = std :: tuple < Args ...>( std :: forward < Args >( args )...)]() mutable { return std :: apply ([]( auto &&... args ) -> std :: optional < S_impl > { try { return std :: optional < S_impl >( std :: in_place , std :: forward < Args >( args )...); } catch (...) { return std :: nullopt ; } }, std :: move ( args )); }() ) { } };

The code converts a class with a throwing constructor to a wrapper class that won’t throw. Such wrapper derives from std::optional<T> so you can directly check if the value is there or not.

Getting File contents

by Michael Cook

full code @Coliru

std :: optional < std :: string > get_file_contents ( std :: string const & filename ) { std :: ifstream inf { filename }; if (! inf . is_open ()) return std :: nullopt ; return std :: string { std :: istreambuf_iterator <char> { inf }, {}}; } int main () { if ( auto stat = get_file_contents ( "/proc/self/stat" )) std :: cout << "stat " << * stat << '

' ; else std :: cout << "no stat

" ; if ( auto nsf = get_file_contents ( "/no/such/file" )) std :: cout << "nsf " << * nsf << '

' ; else std :: cout << "no nsf

" ; }

Haskell’s listToMaybe

From Zachary

Full code @Compiler Explorer

template < typename T > using Opt = std :: optional < T >; using std :: begin ; // listToMaybe :: [T] -> Opt<T> template < typename T , template <typename> typename Cont > auto listToMaybe ( Cont < T > const & xs ) -> Opt < T > { return xs . empty () ? Opt < T >{} : Opt < T >{ *( begin ( xs ) ) }; } auto f () { auto as = std :: vector <int> {}; std :: cout << listToMaybe ( as ). value_or ( 0 ) << '

' ; // 0 }

Haskell listToMaybe documentation.

Cleaner interface for map.find

Vincent Zalzal make a simple, yet handy extension to .std::map Rather than checking for map::end you can use optional.

the full code @Coliru

// Provide an iterator-free interface for lookups to map-like objects. // Warning: the output value is copied into the optional. template < typename Map , typename Key > auto lookup ( const Map & m , const Key & k ) { auto it = m . find ( k ); return it != m . end () ? std :: make_optional ( it -> second ) : std :: nullopt ; } int main () { const std :: map < int , int > squares = { { 1 , 1 }, { 2 , 4 }, { 3 , 9 }, { 4 , 16 } }; // cleaner, no need for != end() if ( const auto square = lookup ( squares , 2 )) { std :: cout << "Square is " << * square << '

' ; } else { std :: cout << "Square is unknown.

" ; } }

Comparing against map::end is sometimes ugly, so wrapping the search into optional looks nice.

I wonder if there are plans to apply optional/variant/any to API in STL. Some overloads would be an excellent addition.

Configuration of a Nuclear Simulation

This comes from Mihai Niculescu who used optional in the configuration of a nuclear simulator.

class ParticleRecord { friend std :: istream & operator >> ( std :: istream & is , ParticleRecord & p ); public : double x () const { return x ; } double y () const { return y ; } double z () const { return z ; } double px () const { return px ; } double py () const { return py ; } double pz () const { return pz ; } double mass () const { return mass ; } const std :: optional <extendedInfo> & extendedInfo () const { return extendedData ; } private : void setExtended ( double tdecay , double tformation , long uniqueId ) { extendedInfo einfo ; einfo . tdec = tdecay ; einfo . tform = tformation ; einfo . uid = uniqueId ; extendedData = einfo ; } double x , y , z ; // position (x,y,z) double px , py , pz ; // momentum (px, py, pz) double mass ; // mass // extended data is available when Sim's parameter 13 is ON std :: optional <extended_fields> extendedData ; };

A natural choice for values that might not be available. Here, if the extendedData is loaded, then the simulation will behave differently.

Factory

This comes from Russell Davidson.

using namelist = std :: vector < std :: string >; template < typename Product > struct basicFactory : boost :: noncopyable { virtual ~ basicFactory () {} virtual bool canProduce ( const std :: string & st ) const = 0 ; virtual std :: optional < Product > produce ( const std :: string & st ) const = 0 ; virtual namelist keys () const = 0 ; }; template < typename T , typename RetType , typename Product , typename Converter > class objFactory : public basicFactory < Product >, public Converter { const Data :: Lookup < T , RetType >* tbl_ ; public : objFactory ( const Data :: Lookup < T , RetType >* tbl ) : tbl_ ( tbl ) {} bool canProduce ( const std :: string & key ) const { return tbl_ -> ismember ( key ); } std :: optional < Product > produce ( const std :: string & key ) const { RetType ret = tbl_ -> find ( key ); if (! ret ) return std :: nullopt ; return std :: make_optional < Product >( Converter :: convert ( ret )); } namelist keys () const { return tbl_ -> keys (); } };

The key method is std::optional<Product> produce(const std::string& key) const which returns a created Products or nullopt .

Summary

Once again thanks for all of the submissions. There are many ways how you can use a particular helper type - in this case std::optional . By looking at real-life examples, you can hopefully learn more.

Do you have any comments regarding the examples? Would you suggest some changes/improvements? Let us know.