1

I am trying to use an std::optional to late instantiate an object (which is not valid before). I found an annoying situation, where I do not know how to elegantly solve this.

I have the following data structure:

struct Foo {
    int foo1;
    float foo2;
};

as a member std::optional<Foo> foo_.

In a function

void Bar::bar(int const value1, float const value2) {
    foo_.emplace(value1, value2);
}

To my surprise, this fails to compile (in GCC 7.1) because it tries to call the constructor of Foo with int const&, float const&. Now naive me tried to specialize emplace as:

foo_.emplace<int, float>(value1, value2);

which did not work either because it tries to use initializer_list then.

So my question is how does one call emplace elegantly?

abergmeier
  • 13,224
  • 13
  • 64
  • 120

2 Answers2

8

You have to add constructor as emplace use () constructor and not {} (which would allow aggregate initialization).

struct Foo {
     Foo(int i, float f) : foo1(i), foo2(f) {}

    int foo1;
    float foo2;
};

Or be explicit on constructor used:

foo_.emplace(Foo{value1, value2});
Jarod42
  • 203,559
  • 14
  • 181
  • 302
0

You can use the following wrapper to wrap the aggregate:

template< typename type >
struct aggregate_wrapper
    : type
{

    aggregate_wrapper() = default;

    using type::operator =;

    template< typename ...arguments,
              bool is_noexcept = noexcept(::new (std::declval< void * >()) type{std::declval< arguments >()...}) >
    constexpr
    aggregate_wrapper(arguments &&... _arguments) noexcept(is_noexcept)
        : type{std::forward< arguments >(_arguments)...}
    { ; }

};

In manner std::optional< aggregate_wrapper< Foo > >.

For allocator-aware containers like std::vector you can use modification of an allocator like mentioned here (i.e. with curly braces inside of construct instead of parentheses).

Tomilov Anatoliy
  • 15,657
  • 10
  • 64
  • 169