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?
–
–
–
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 :)
–
–
–
–
–
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.