5

See Should I use () or {} when forwarding arguments?. foo is a std::vector clone.

In N4140, unique.ptr.create std::make_unique is specified as so:

template <class T, class... Args> unique_ptr<T> make_unique(Args&&... args);

  • Remarks: This function shall not participate in overload resolution unless T is not an array.

  • Returns: unique_ptr<T>(new T(std::forward<Args>(args)...)).

That means implementations are required to use () rather than {} to initialize objects. As an example, the following

auto s1 = std::make_unique<foo>(3, 1).get()->size();
auto s2 = std::make_unique<foo>(1).get()->size();
auto s3 = std::make_unique<foo>(2).get()->size();
std::cout << s1 << s2 << s3;

outputs 312 whereas if {} (inside std::make_unique) was used 211 would be output. Because initializer lists cannot be deduced, std::initializer_list must be explicitly passed in order to get the latter result. The question is, why isn't an overload like this provided?

namespace test
{

template <class T, class Deduce>
std::unique_ptr<T> make_unique(std::initializer_list<Deduce> li)
{
    return ::std::make_unique<T>(li);
}

};

int main()
{
    auto p1 = test::make_unique<foo>({3, 1}).get()->size();
    auto p2 = test::make_unique<foo>({1}).get()->size();
    auto p3 = test::make_unique<foo>({2}).get()->size();
    std::cout << p1 << p2 << p3;
}

Outputs 211.

I don't consider the reasons "you can write it yourself" or "to avoid bloating the standard" to be very good reasons. Is there any disadvantages to providing this overload?

2 Answers2

2

I don't know the complete history, but the most likely answer is "nobody proposed it".

Evidence for this is supported by the fact that std::make_unique was only added in C++14, yet std::unique_ptr existed in C++11.

The code and proposals for std::make_shared (which was eventually mirrored in make_unique) existed (in boost) prior to the initializer_list and the initialisation syntax supporting it.

One thing you could do now, is to propose it (and work out the corner cases if there are any, e.g. use SFINAE to remove the overload if the target type doesn't support the initializer_list).

Niall
  • 30,036
  • 10
  • 99
  • 142
  • I don't think SFINAE is necessary. `make_unique>(1, 73);` works perfectly fine if I take it out of the namespace and do `using namespace std;` (to make sure both overloads are visible). If somebody does `make_unique>({1, 73});` that's their fault. – user6319825 May 11 '16 at 11:45
  • @user6319825. Yes, the SFINAE is not essential, the compile fails either way, just in my experience the error messages in a SFINAE environment are "easier" to diagnose. – Niall May 11 '16 at 11:51
  • Still. They deliberate said `new T(std::forward(args)...)` rather than leave it up to the implementation like for [optional](http://eel.is/c++draft/optional.object.ctor#23): "Initializes the contained value as if direct-non-list-initializing an object of type `T` with the arguments `std::forward(args)`...." That makes me think there's some discussion *somewhere* at least for `make_unique` specifically. – user6319825 May 11 '16 at 11:59
  • Interesting to note; [`optional` caters for the `initializer_list`](http://eel.is/c++draft/optional.object.ctor#31) – Niall May 11 '16 at 12:03
  • @user6319825. The wording is different, probably had different authors for each section. I like the idea of the overload, it would be handy. Probably worth proposing somehow. – Niall May 11 '16 at 12:05
  • @Niall I find that overload is awesome to make perfect forwarding more perfect. I wonder if I learned it from an early optional proposal? – Yakk - Adam Nevraumont May 11 '16 at 13:23
  • @Yakk. Possibly. I like some of the tag techniques used in the code that came through around about that time as well. – Niall May 11 '16 at 13:40
0

I think it is just a template deduction issue, see this answer.

You could try these:

int main()
{
    auto p1 = std::make_unique<foo>(std::initializer_list<int>{3, 1}).get()->size();
    auto p2 = std::make_unique<foo>(foo{1}).get()->size();
    auto l3 = {2};
    auto p3 = std::make_unique<foo>(l3).get()->size();
    std::cout << p1 << p2 << p3;
}
Community
  • 1
  • 1
user1887915
  • 1,299
  • 10
  • 13