1

The std::make_ functions in the standard, such as:

  • std::make_unique and std::make_shared
  • std::make_tuple
  • std::make_from_tuple

all use internally round brackets initialization rather than curly brackets.

For example, make_from_tuple as presented by the standard is choosing to return T(params...) rather than T{params...}.

The result is that the following are illegal:

auto vec = std::make_from_tuple<std::vector<int>>(std::make_tuple());
auto arr = std::make_from_tuple<std::array<int, 2>>(std::make_tuple(9, 8));

^ the creation of std::array from a tuple, as above, is illegal also with C++20, as p0960 - allowing initialization of aggregates from a parenthesized list of values becoming part of the C++20 spec doesn't allow such initialization for std::array, as its inner type is T[size] which cannot be initialized from a list of values (the parentheses are already being stripped by std::array initialization).


In cases where it does work, the selection of brackets initialization vs. curly brackets is meaningful:

auto vec2 = std::make_from_tuple<std::vector<int>>(std::make_tuple(2, 3));
// a vector with the values: {3, 3} surprise? :-)

(Above are of course toy examples. The supplied tuple may be provided externally).

With a curly_make_from_tuple like:

template<typename T, typename tuple_t>
constexpr auto curly_make_from_tuple(tuple_t&& tuple) {
    constexpr auto get_T = [](auto&& ... x){ return T{std::forward<decltype(x)>(x) ... }; };
    return std::apply(get_T, std::forward<tuple_t>(tuple));
}

all cases above would work, in a way that one may argue is more natural:

auto arr = curly_make_from_tuple<std::array<int, 2>>(std::make_tuple(9, 8)); // {9, 8}
auto vec = curly_make_from_tuple<std::vector<int>>(std::make_tuple());       // {}
auto vec2 = curly_make_from_tuple<std::vector<int>>(std::make_tuple(2, 3));  // {2, 3}

The question is: why the standard chose round brackets initialization over curly brackets?


Related links:

Similar question, from efficiency point of view: Why does implementation of make_tuple not return via brace initialisation?

A nice discussion and suggestions for adding curly brackets initialization option for the `make_` utilities.

The original paper proposing make_from_tuple, P0209r2, seem not to discuss the two alternatives T(params...) and T{params...}, maybe because all similar make_ utility methods had already been using round brackets initialization.

Amir Kirsh
  • 12,564
  • 41
  • 74
  • 2
    Function call always uses round brackets. That's the language syntax. The curly brackets are for initializer lists. – sanitizedUser May 26 '20 at 19:27
  • Are you askig why they use `T(std::forward(x) ... )` instead of `T{std::forward(x) ... }`? – NathanOliver May 26 '20 at 19:30
  • @NathanOliver yes, ignoring fancy options to support both (as discussed in the 2nd link provided at the end) if one should be supported, why the round brackets? – Amir Kirsh May 26 '20 at 19:39
  • 1
    It's not an initialization, it's a function call `std::make_unique`, `std::make_shared`, `std::make_tuple`, etc. don't initialize things, they return the objects and the advantages of them are thread-safety, simplicity, etc. – asmmo May 26 '20 at 20:11
  • @asmmo the question is on the ***internal*** implementation of these functions. – Amir Kirsh May 26 '20 at 20:12
  • Another related question, with several interesting answers, can be found here: https://stackoverflow.com/questions/55141594/make-unique-with-brace-initialization – Yehezkel B. May 26 '20 at 20:23

1 Answers1

1

Because it was not possible to initialize structure using braced-init-list in C++98.

So for consistency new standandard library features have used the same initialization form as the one used in the STL.

More over, it has never been changed to list-initialization for compatibility reason: list-initialization has not necessarily the same meaning as the equivalent parenthesized initialization form.

Oliv
  • 17,610
  • 1
  • 29
  • 72
  • Which library function prior to C++11 was creating an object like that? Probably none, since it relies on variadic template. So if all such functions just appeared in C++11 or after, it could have used brace-initialization. Sure one has to be selected if not using a fancy approach to support both, but did the committee considered both? Was it an _intentioned decision_? – Amir Kirsh May 27 '20 at 05:02
  • @AmirKirsh For exemple the STL allocator, for copy construction. – Oliv May 27 '20 at 06:22