Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

Assume namespace std throughout.

The C++14 committee draft N3690 defines std::make_unique thus:

[n3690: 20.9.1.4]: unique_ptr creation [unique.ptr.create]

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

1 Remarks: This function shall not participate in overload resolution unless T is not an array.
2 Returns: unique_ptr<T>(new T(std::forward<Args>(args)...)).

template <class T> unique_ptr<T> make_unique(size_t n);

3 Remarks: This function shall not participate in overload resolution unless T is an array of unknown bound.
4 Returns: unique_ptr<T>(new typename remove_extent<T>::type[n]()).

template <class T, class... Args> unspecified make_unique(Args&&...) = delete;

5 Remarks: This function shall not participate in overload resolution unless T is an array of known bound.

Now, this seems to me to be about as clear as mud, and I think it needs more exposition. But, this editorial comment aside, I believe I've decoded the meanings of each variant:

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

    Your bog-standard make_unique for non-array types. Presumably the "remark" indicates that some form of static assertion or SFINAE trick is to prevent the template from being successfully instantiated when T is an array type.

    At a high-level, see it as the smart-pointer equivalent to T* ptr = new T(args); .

  • template <class T> unique_ptr<T> make_unique(size_t n);

    A variant for array types. Creates a dynamically-allocated array of n × Ts , and returns it wrapped in a unique_ptr<T[]> .

    At a high-level, see it as the smart-pointer equivalent to T* ptr = new T[n]; .

  • template <class T, class... Args> unspecified make_unique(Args&&...)

    Disallowed. " unspecified " would probably be unique_ptr<T[N]> .

    Would otherwise be the smart-pointer equivalent to something like the invalid T[N]* ptr = new (keep_the_dimension_please) (the_dimension_is_constexpr) T[N]; .

  • First of all, am I correct? And, if so, what's going on with the third function?

  • If it's there to disallow programmers from attempting to dynamically-allocate an array while providing constructor arguments for each element (just as new int[5](args) is impossible), then that's already covered by the fact that the first function cannot be instantiated for array types, isn't it?

  • If it's there to prevent the addition to the language of a construct like T[N]* ptr = new T[N] (where N is some constexpr ) then, well, why? Wouldn't it be completely possible for a unique_ptr<T[N]> to exist that wraps a dynamically-allocated block of N × T s? Would this be such a bad thing, to the extent that the committee has gone out of its way to disallow its creation using make_unique ?

  • Why is make_unique<T[N]> disallowed?

    "This function shall not participate in overload resolution unless T is not an array." - yeah... zch May 16, 2013 at 20:45 @Mehrdad: I agree! Fortunately your proposal is easy to follow, since nobody's been doing that so far. Lightness Races in Orbit May 16, 2013 at 21:12 Instead of T[N]* ptr = new (keep_the_dimension_please) (the_dimension_is_constexpr) T[N] you would write, in not-quite conformant code: T (*ptr)[N] = reinterpret_cast<T(*)[N]>(new T[N]); The non-conformance is because, although there's a special rule that says you can convert a pointer to the first element of a standard-layout struct to a pointer to a struct, there's no similar rule for arrays. This code is portable across real implementations, however. bames53 May 16, 2013 at 22:02

    As of N3485, unique_ptr doesn't provide a partial specialization for T[N] . However, users will be strongly tempted to write make_unique<T[N]>() . This is a no-win scenario. Returning unique_ptr<T[N]> would select the primary template for single objects, which is bizarre. Returning unique_ptr<T[]> would be an exception to the otherwise ironclad rule that make_unique<something>() returns unique_ptr<something> . Therefore, this proposal makes T[N] ill-formed here, allowing implementations to emit helpful static_assert messages.

    The author of the proposal, Stephan T. Lavavej, illustrates this situation in this video on Core C++ (courtesy of chris ), starting from minute 1:01:10 (more or less).

    To me the third overload looks superfluous as does not change the fact that the other overloads won't match T[N] and it does not seem to help to generate better error messages. Consider the following implementation:

    template< typename T, typename... Args >
    typename enable_if< !is_array< T >::value, unique_ptr< T > >::type
    make_unique( Args&&... args )
      return unique_ptr< T >( new T( forward< Args >( args )... ) );
    template< typename T >
    typename enable_if< is_array< T >::value && extent< T >::value == 0, unique_ptr< T > >::type
    make_unique( const size_t n )
      using U = typename remove_extent< T >::type;
      return unique_ptr< T >( new U[ n ]() );
    

    When you try to call std::make_unique<int[1]>(1), the error message lists both candidates as disabled by enable_if. If you add the third, deleted overload, the error message lists three candidates instead. Also, since it is specified as =delete;, you can not provide a more meaningful error message in the third overload's body, e.g., static_assert(sizeof(T)==0,"array of known bound not allowed for std::make_shared");.

    Here's the live example in case you want to play with it.

    The fact that the third overload ended up in N3656 and N3797 is probably due to the history of how make_unique was developed over time, but I guess only STL can answer that :)

    To the commenter that deleted the comment: The error messages varies with the compiler, but it seems to be best for GCC/Clang to have a third overload with a static_assert to get short and to-the-point error messages. – Daniel Frey Dec 1, 2013 at 16:12 I played with the live example. With the 3 overloads: error: call to deleted function 'make_unique' (+ a “candidate function (...) has been explicitly deleted” note + 2 “candidate template ignored: disabled by 'enable_if'” notes). With only the first 2 overloads: error: no matching function for call to 'make_unique', which I find more confusing (+ 2 “candidate template ignored: disabled by 'enable_if'” notes). (And that's for clang++ (3.4); with g++ (4.8), when you remove the third overload, in fact the error message becomes longer.) Now a static_assert could indeed be even better. – gx_ Dec 1, 2013 at 16:16 @gx_ I wonder if the standard should just allow implementations to provide additional overloads that are either deleted or have an appropriate static_assert in general to improve error messages - not just in this specific example, but everywhere... – Daniel Frey Dec 1, 2013 at 16:19 Second thought about static_assert: If you replace your live examples' main body with something like decltype( experimental::make_unique<int[1]>(42) ) * p = nullptr;, then with the static_assert alternative it would compile without error, whereas with =delete (or when removing the third overload) it causes the expected error. Edit: So regarding 17.6.5.4, it seems that it would be an observable difference of behavior – gx_ Dec 1, 2013 at 16:38 @gx_ Note that 17.6.5.4/2 explicitly starts with "A call to...", see also footnote 187) which clarifies that a non-call/unevaluated context may see additional overloads. That said, explicitly specifying the third overload for make_unique seems counter-productive. – Daniel Frey Dec 1, 2013 at 21:43

    Thanks for contributing an answer to Stack Overflow!

    • Please be sure to answer the question. Provide details and share your research!

    But avoid

    • Asking for help, clarification, or responding to other answers.
    • Making statements based on opinion; back them up with references or personal experience.

    To learn more, see our tips on writing great answers.