2

How do I rewrite the following code to use boost::optional or boost::none, in C++11?

std::unique_ptr<FooBase> find( std::string key)
{
    std::map<std::string, std::function<std::unique_ptr<FooBase>(void)> > m{
                       {"key1", [](){return std::make_unique<BarDerived>();} },
                       {"key2", [](){return std::make_unique<BarDerived1>();} } };
                                        
    auto it = m.find(key);
    if (it != std::end(m))
        return (it->second());  
    else 
        return nullptr;                                        

}
Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
javauser
  • 31
  • 1
  • I am not sure whether I would wrap a smart pointer into an `optional`. `std::unique_ptr` already has a `null`-state, so you would basically double that. Imagine: `if (foo && *foo)` is required to make sure the optional has a value and the wrapped smart pointer is alive... – lubgr Oct 07 '20 at 10:05
  • make_unique is not c++11 btw – Yamahari Oct 07 '20 at 11:11
  • Maybe this [answer](https://stackoverflow.com/questions/46299607/mix-boostoptional-and-stdunique-ptr) helps – sfun Oct 07 '20 at 11:22
  • also you might consider not using std::function at all, switching to unordered_map and if you're able to use c++17 switching to string_view : https://godbolt.org/z/xEvG5P compare the disassmbly of all 3 versions and see for yourself – Yamahari Oct 07 '20 at 12:55
  • 1
    @Yamahari before C++17 you can already use [boost::string_ref](https://www.boost.org/doc/libs/1_72_0/libs/utility/doc/html/string_ref.html) or [boost::string_view](https://www.boost.org/doc/libs/1_72_0/boost/utility/string_view.hpp) – phuclv Oct 07 '20 at 23:36

1 Answers1

1

So, you want this to return a value type instead of a pointer?

That isn't possible with boost::optional (or std::optional in c++17), because of object slicing. For a value type, you can only return as much information as FooBase contains, so you'll lose information when you upcast from one of the derived types.

For this though, you can use another Boost type that got adopted by the C++17 standard: boost::variant. This is a type-safe tagged union that can hold one of a set of types in the same memory space. Just add a type to represent "none" (std::monostate's purpose in C++17, and boost::blank's purpose in Boost), and then add each derived type:

struct Bar1 { };
struct Bar2 { };
using Bar = boost::variant<boost::blank, Bar1, Bar2>;

Then you can rewrite your function like this:

Bar find( std::string key)
{
    std::map<std::string, std::function<Bar()> > m {
        {"key1", [](){return Bar1 {}; } },
        {"key2", [](){return Bar2 {}; } } 
    };
                                        
    auto it = m.find(key);
    if (it != std::end(m))
        return (it->second());  
    else 
        return { }; // default-constructs the first variant, in this case blank
}

https://godbolt.org/z/dcYevv

parktomatomi
  • 3,851
  • 1
  • 14
  • 18