bolov
bolov

Reputation: 75854

What is the point of `std::make_optional`

All the std::make_ are made redundant by C++17 with the introduction of Class template argument deduction (except make_unique and make_shared).

So what is the point of std::make_optional? As far as I can tell it does the exact same thing as the deduction guides for std::optional.

Is there a scenario where std::make_optional is preferred over deduction guides?

Upvotes: 35

Views: 13532

Answers (2)

jfMR
jfMR

Reputation: 24788

Another example of a use for std::make_optional() would be for constructing the object stored in a std::optional without creating a temporary for it when its constructor takes multiple arguments.

For example, consider the following Point3D class whose constructor has multiple arguments:

struct Point3D {
   Point3D(int xx, int yy, int zz): x(xx), y(yy), z(zz) {}
   int x, y, z;
};

Imagine that you want to initialize the object stored by a std::optional<Point3D> to Point3D(1, 2, 3)X, then you could proceed as:

std::optional oPoint = Point3D(1, 2, 3);

However, this would create a Point3D temporary object that is then moved into the std::optional. Instead, by using the std::make_optional() convenience function template you can avoid the creation of that temporary:

auto oPoint = std::make_optional<Point3D>(1, 2, 3);

Since we are talking about C++17 here (i.e., std::optional was introduced in C++17), guaranteed copy elision applies here, and therefore no Point3D temporary object is created.


Note that you can still avoid the creation of the temporary object without using std::make_optional() by passing std::in_place to the constructor of the std::optional<Point3D> object:

std::optional<Point3D> oPoint{std::in_place, 1, 2, 3};

This will construct the stored Point3D object in place, thus avoiding the creation of a temporary. Nevertheless, you may find this more verbose than the approach with std::make_optional().


Xstd::optional<Point3D> oPoint(1, 2, 3); doesn't compile.

Upvotes: 6

Ruslan
Ruslan

Reputation: 19140

One example of the difference is when you want (for whatever reason) to make an optional containing an optional:

#include <optional>
#include <type_traits>

int main()
{
    auto inner=std::make_optional(325);
    auto opt2=std::make_optional(inner); // makes std::optional<std::optional<int>>
    auto opt3=std::optional(inner);      // just a copy of inner
    static_assert(std::is_same_v<decltype(opt2), std::optional<std::optional<int>>>);
    static_assert(std::is_same_v<decltype(opt3), std::optional<int>>);
}

Upvotes: 24

Related Questions