Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Avoiding temporary when using boost::optional

boost::optional support in_place construction like so:

#include <boost/optional.hpp>
#include <boost/utility/typed_in_place_factory.hpp>

class Foo
{
    int a,b;

  public:
    Foo(int one, int two) : a(one),b(two) {}
};


int main()
{
    boost::optional<Foo> fooOpt(boost::in_place<Foo>(1,3));
}

Once we have an initialized fooOpt, is there a way of assigning a new Foo to it without creating a temporary?

Something like :

fooOpt = boost::in_place<Foo>(1,3);

Thanks!

like image 954
MK. Avatar asked Aug 07 '14 14:08

MK.


2 Answers

boost::optional

#include <boost/optional.hpp>

int main() {
    boost::optional<int> x;
    x = boost::in_place(3);
}

We can also show (via code) that this is building the object in-place by making Foo inherit from boost::noncopyable:

#include <boost/optional.hpp>
#include <boost/noncopyable.hpp>

class Foo : boost::noncopyable {
    public:
        Foo(int one, int two) {}
};


int main() {
    boost::optional<Foo> x;
    x = boost::in_place(3, 4);
}

std::optional (eventually...)

Eventually, we will get access to std::optional. This type will implement an emplace() method, that will implement in-place construction as well.

#include <optional>

int main() {
    std::optional<int> x;
    x.emplace(3);
}

boost::optional (soon...)

In version 1.56.0, boost::optional will also implement the emplace() method that I talked about for std::optional. So, let's see that:

#include <boost/optional.hpp>

int main() {
    boost::optional<int> x;
    x.emplace(3);
}
like image 106
Bill Lynch Avatar answered Nov 13 '22 07:11

Bill Lynch


The documented interface does not support this.

However, if you know that nobody extends boost::optional, I believe this may be technically valid:

template<typename T, typename... Us>
void emplace_replace( boost::optional<T>& target, Us&&... us ) {
  target.~boost::optional<T>();
  try {
    new (&target) boost::optional<T>( boost::in_place( std::forward<Us>(us)...) );
  } catch(...) {
    new (&target) boost::optional<T>();
    throw;
  }
}

Here, we destroy the target, then reconstruct a new boost::optional<T> in its place with in-place construction. The try-catch construct should make most throws during construction safe: if it throws, you end up with an empty optional.

This naturally behaves differently than operator= is expected to.


In 1.55 (and maybe earlier?), there is an undocumented operator= that takes an Expr which supports boost::in_place and boost::in_place<T>. See @sharth's answer for a detailed use.

My quick reading indicates that a typed inplace factory via this method may have insufficient guards:

boost::optional<int> test;
// test = boost::in_place<double>( 3 ); // <-- very dangerous
test = boost::in_place( 3 ); // safe
test = boost::in_place( 3.0 ); // safe

If a type is passed directly to in_place<?>, it can generate a typed_in_place_factory, which are dangerous (they make the passed in type, and don't check that it is compatible). So don't pass any types to boost::in_place.

This (from reading the source) does something similar to my destroy/reconstruct code, except it does it without destroying the entire optional and just destroys the stored data and makes it uninitialized.


In boost 1.56b1, emplace has been added to boost::optional. It does something similar to both of the above operations. (via @AkiraTakahashi)


std::optional proposals I have seen have included a member function .emplace( Us&&... ) that supports emplace replacing directly.

like image 3
Yakk - Adam Nevraumont Avatar answered Nov 13 '22 06:11

Yakk - Adam Nevraumont