简体   繁体   中英

Consistency between std::make_pair and std::make_optional

My initial idea always had been to try using make-helper functions whenever possible (also because they neatly align with the almost-always-auto concept and east-initialization convention) while at the same time trying to be optimal with regard to the number of copies and moves that can be avoided. Consider for instance a pair of two default-constructible Widgets

#include<utility>
#include<iostream>

struct Widget{
  Widget() { std::cout << "Default ctor\n"; }
  explicit Widget(int i) : i_{i} { std::cout << "Custom ctor\n"; }
  int i_;
};

auto p = std::make_pair<Widget,Widget>({},{});

But this syntax is clumsy and looks messy and std::pair's constructor allows for a more concise syntax here due to the fact that the two Widgets are default-constructed within the pair

auto p = std::pair<Widget, Widget>{};

Then I learned that std::make_pair has basically been rendered obsolete with C++17 class template argument deduction capabilities and deduction guides, is that correct? If so, how would one go about initialize the pair of Widgets with the constructor taking an integer without explicitly specifying template arguments?

And so I turned to lines in my code base and tried to also replace occurrences of std::make_optional

auto o = std::make_optional<Widget>{}

into std::optional when the trouble began

auto o = std::optional<Widget>{};

While the first option creates a default-constructed Widget within the optional, the second variant merely creates an empty optional. There is an alternative with makes the construction more wordy though: std::in_place. Also there always is the option to use emplace() but this makes the code a two-liner. Now I am torn between keep using std::make_optional and using the verbose option

auto o = std::optional<Widget>{std::in_place};

Neither of which I am particularly satisfied with. How would you go about this issue?

As you have noticed, the make functions aren't rendered obsolete by class template argument deduction.

The fact that there is a difference between a value created by make and a default constructed wrapper is what you're seeing here.

Another example is with make_tuple :

auto t = std::make_tuple(42);
auto tt = std::make_tuple(t);

This is not the same as

auto t = std::tuple{42};
auto tt = std::tuple{t};

The latter will call the copy constructor of t , resulting in a std::tuple<int> , while in the make_tuple case tt was a std::tuple<std::tuple<int>> .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM