c++boostc++17

Fastest way to convert boost::optional to std::optional


I need to use boost libraries in my project, such as boost::property_tree and boost::convert, which in turn use boost::optional. At the same time, the interface I'm implementing uses std::optional. What is the fastest way to convert boost::optional to std::optional?

There is my suggestion

template<class T>
inline std::optional<T> ToStd(boost::optional<T>&& opt) {
  if (opt) {
    return std::move(opt.value());
  } else {
    return std::nullopt;
  }
}

Is there a faster way?


Solution

  • Since one would most likely want to be able to pass this ToStd entity to a higher-order function, such as an STL algorithm, e.g.

    std::transform(someBoostOpts.begin(),
                   someBoostOpts.end(),
                   resultingStdOpts.begin(),
                   ToStd);
    

    it would be better to make it a function object, so that its name is not shared across multiple instatiations/overloads. (If you try the above snippet with your ToStd, you'll see a compilation failure along the lines of "I don't know which templ.instantiation/overload you are referring to".)

    To do so, the most practical way is to write it as a lambda:

    inline constexpr auto BoostOptToStdOpt = [](auto&& opt) {
      if (opt.has_value()) {
        return std::make_optional(std::forward<decltype(opt)>(opt).value());
      } else {
        return std::nullopt;
      }
    };
    

    The .has_value() and .value() will cause a compilation failure if you try to use it with something that doesn't have those member functions (but it will also "work", i.e. doing a usless job, if you pass to it a std::optional).

    If you really want a nice error if you pass to it a non-boost::optional, then you can write a type trait and static_assert.


    As regards the question of whethere there's a faster way, I wouldn't expect one exists, for a simple reason: std::optional and boost::optional occupy precisely the same place in C++, the latter being the older, experimental solution, and the latter the standardised version that supersedes it.

    It's not like, say, having such a handy way to convert a std::unique_ptr to std::shared_ptr (i.e. you can std::shared_ptr<T> s = some_unique_ptr_to_T;); such a construtor exists because the two types fulfill different necessities, and it is semantically sensible to transform a std::unique_ptr into a std::shared_ptr (and to explicitly forbid the inverse conversion).

    std::optional and boost::optional have the same identical semantics (at least in the intentions), and providing a standard way to move from one to another would even fool you into thinking that they fulfill different needs, but that would be untrue.

    Ideally, you'd want every program to be renewed and to stop using boost::optional in favour of std::optional.