Почему `make_unique & lt; T [N] & gt;` запрещено?

Предположим, пространство имен std на протяжении.

Проект комитета C ++ 14 N3690 определяет std::make_unique таким образом:

[n3690: 20.9.1.4]: unique_ptr создание    [Unique.ptr.create]

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

1 Примечания: эта функция не должна участвовать в разрешении перегрузки, если только T это не массив.
2 Возвращает: unique_ptr<T>(new T(std::forward<Args>(args)...)).

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

3 Примечания: эта функция не должна участвовать в разрешении перегрузки, если только T массив неизвестных границ
4 Возвращает: unique_ptr<T>(new typename remove_extent<T>::type[n]()).

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

5 Примечания: эта функция не должна участвовать в разрешении перегрузки, если только T массив известных границ

Теперь мне кажется, что это почти так же ясно, как грязь, и я думаю, что это требует большей экспозиции. Но, если оставить в стороне этот редакционный комментарий, я думаю, что я расшифровал значения каждого варианта:

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

    Ваш болотный стандарт make_unique для не-массив типов. Предположительно, «замечание» указывает на то, что некоторая форма статического утверждения или трюка SFINAE состоит в том, чтобы предотвратить успешное создание шаблона, когда T тип массива

    На высоком уровне рассматривайте его как смарт-указатель, эквивалентный T* ptr = new T(args);,

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

    Вариант для типов массивов. Создает динамически распределенный массив n × Tsи возвращает его, завернутый в unique_ptr<T[]>,

    На высоком уровне рассматривайте его как смарт-указатель, эквивалентный T* ptr = new T[n];,

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

    Недопустимое. «неопределенные«вероятно будет unique_ptr<T[N]>,

    В противном случае смарт-указатель будет эквивалентен чему-то недопустимому T[N]* ptr = new (keep_the_dimension_please) (the_dimension_is_constexpr) T[N];,

Прежде всего, я прав? И если так, что происходит с третьей функцией?

  • Если он запрещает программистам пытаться динамически размещать массив, предоставляя аргументы конструктора для каждого элемента (так же, как new int[5](args) невозможно), тогда это уже покрыто тем фактом, что первая функция не может быть реализована для типов массивов, не так ли?

  • Если это там, чтобы предотвратить добавление к языку конструкции, как T[N]* ptr = new T[N] (где N это какой-то constexpr) ну и зачем? Разве это не было бы полностью возможно для unique_ptr<T[N]> существовать, которое оборачивает динамически распределенный блок N × Ts? Будет ли это настолько плохой вещью, что комитет изо всех сил пытается запретить его создание, используя make_unique?

Почему make_unique<T[N]> запрещено?

25

Решение

Цитировать из оригинальное предложение:

T[N]

По состоянию на N3485, unique_ptr не обеспечивает частичную специализацию для T[N],
Тем не менее, пользователи будут сильно склонны писать make_unique<T[N]>(), это
это беспроигрышный сценарий. возврате unique_ptr<T[N]> выбрал бы основной
шаблон для отдельных объектов, что странно. возврате unique_ptr<T[]>
будет исключением из ироничного правила, что
make_unique<something>() возвращается unique_ptr<something>, Следовательно, это
предложение делает T[N] плохо сформированный здесь, позволяющий реализации излучать
полезный static_assert Сообщения.

Автор предложения, Стефан Т. Лававей, иллюстрирует эту ситуацию в это видео на Core C ++ (любезно предоставлено Крис), начиная с минуты 1:01:10 (более или менее).

32

Другие решения

Для меня третья перегрузка выглядит излишней, так как не меняет того факта, что другие перегрузки не будут совпадать T[N] и это делает не кажется, помогают генерировать лучшие сообщения об ошибках. Рассмотрим следующую реализацию:

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 ]() );
}

Когда вы пытаетесь позвонить std::make_unique<int[1]>(1)сообщение об ошибке перечисляет обоих кандидатов как отключенных enable_if, Если вы добавите третью удаленную перегрузку, в сообщении об ошибке будут перечислены три кандидата. Кроме того, так как он указан как =delete;вы не можете предоставить более значимое сообщение об ошибке в теле третьей перегрузки, например, static_assert(sizeof(T)==0,"array of known bound not allowed for std::make_shared");,

Вот живой пример в случае, если вы хотите поиграть с ним.

Тот факт, что третья перегрузка закончилась в N3656 и N3797, возможно, связана с историей того, как make_unique был разработан со временем, но я думаю, что только STL может ответить на это 🙂

0