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.