Подтвердить что ты не робот

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

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

Проект комитета N3690 Комитета С++ 14 определяет 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 не является массивом известной границы.

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

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

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

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

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

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

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

  • 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 & times; T s? Было бы так плохо, если бы комиссия отказалась от своего создания, используя make_unique?

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

4b9b3361

Ответ 1

Цитата из исходное предложение:

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 С++ (любезно предоставлено chris), начиная с минуты 1:01:10 (более или менее).

Ответ 2

Мне третья перегрузка выглядит излишней, так как не меняет того факта, что другие перегрузки не будут соответствовать 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 может ответить на это:)