Make_unique и совершенная переадресация
Почему в стандартной библиотеке С++ 11 нет шаблона функции std::make_unique
? Я нахожу
std::unique_ptr<SomeUserDefinedType> p(new SomeUserDefinedType(1, 2, 3));
немного подробный. Разве не было бы намного лучше?
auto p = std::make_unique<SomeUserDefinedType>(1, 2, 3);
Это скрывает new
красиво и только упоминает тип один раз.
В любом случае, вот моя попытка реализации make_unique
:
template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args)
{
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}
Мне потребовалось некоторое время, чтобы собрать std::forward
материал, но я не уверен, правильно ли это. Это? Что именно означает std::forward<Args>(args)...
? Что делает компилятор из этого?
Ответы
Ответ 1
Херб Саттер, председатель комитета по стандартизации С++, пишет в своем блоге :
То, что С++ 11 не включает make_unique
, частично является надзором, и его почти наверняка добавят в будущем.
Он также дает реализацию, которая идентична реализации, заданной OP.
Изменить: std::make_unique
теперь является частью C++14.
Ответ 2
Хороший, но Stephan T. Lavavej (более известный как STL) имеет лучшее решение для make_unique
, которое корректно работает для версии массива.
#include <memory>
#include <type_traits>
#include <utility>
template <typename T, typename... Args>
std::unique_ptr<T> make_unique_helper(std::false_type, Args&&... args) {
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}
template <typename T, typename... Args>
std::unique_ptr<T> make_unique_helper(std::true_type, Args&&... args) {
static_assert(std::extent<T>::value == 0,
"make_unique<T[N]>() is forbidden, please use make_unique<T[]>().");
typedef typename std::remove_extent<T>::type U;
return std::unique_ptr<T>(new U[sizeof...(Args)]{std::forward<Args>(args)...});
}
template <typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args) {
return make_unique_helper<T>(std::is_array<T>(), std::forward<Args>(args)...);
}
Это можно увидеть на его видео с Core С++ 6.
Обновленная версия STL-версии make_unique теперь доступна как N3656. Эта версия принята в проекте С++ 14.
Ответ 3
В то время как ничто не мешает вам писать свой собственный помощник, я считаю, что основной причиной предоставления make_shared<T>
в библиотеке является то, что он фактически создает другой внутренний тип общего указателя, чем shared_ptr<T>(new T)
, который по-разному распределен, и нет способа достичь этого без специального помощника.
Ваша оболочка make_unique
, с другой стороны, представляет собой простой синтаксический сахар вокруг выражения new
, поэтому, хотя это может показаться приятным для глаз, оно не приносит ничего new
в таблицу. Исправление:. На самом деле это не так: вызов функции для обертывания выражения new
обеспечивает безопасность исключений, например, в случае, когда вы вызываете функцию void f(std::unique_ptr<A> &&, std::unique_ptr<B> &&)
. Наличие двух исходных new
, которые не имеют значения по отношению друг к другу, означает, что если одно новое выражение завершится с исключением, другое может утечка ресурсов. Что касается того, почему в стандарте нет make_unique
: это было просто забыто. (Это случается иногда. В стандарте также нет глобального std::cbegin
, хотя он должен быть таким.)
Также обратите внимание, что unique_ptr
принимает второй параметр шаблона, который вы должны каким-то образом разрешить; это отличается от shared_ptr
, который использует стирание типа для хранения пользовательских удалений, не делая их частью типа.
Ответ 4
std::make_shared
- это не просто сокращение для std::shared_ptr<Type> ptr(new Type(...));
. Он делает то, что вы не можете обойтись без него.
Чтобы выполнить свою работу, std::shared_ptr
должен выделить блок отслеживания в дополнение к хранению для фактического указателя. Однако, поскольку std::make_shared
выделяет фактический объект, возможно, что std::make_shared
выделяет и объект, и блок отслеживания в том же блоке памяти.
Итак, в то время как std::shared_ptr<Type> ptr = new Type(...);
будет иметь два распределения памяти (один для new
, один в блоке отслеживания std::shared_ptr
), std::make_shared<Type>(...)
выделит один блок памяти.
Это важно для многих потенциальных пользователей std::shared_ptr
. Единственное, что сделал бы std::make_unique
, было бы немного более удобным. Не более того.
Ответ 5
В С++ 11 ...
используется (в коде шаблона) для "расширения пакета".
Требование состоит в том, что вы используете его как суффикс выражения, содержащего нерасширенный пакет параметров, и он просто применит выражение к каждому из элементов пакета.
Например, построив пример:
std::forward<Args>(args)... -> std::forward<int>(1), std::forward<int>(2),
std::forward<int>(3)
std::forward<Args...>(args...) -> std::forward<int, int, int>(1,2,3)
Последнее неверно, я думаю.
Кроме того, пакет аргументов не может быть передан функции, нерасширенной. Я не уверен в пакете параметров шаблона.
Ответ 6
Вдохновленный реализацией Stephan T. Lavavej, я подумал, что было бы неплохо иметь make_unique, поддерживавший размеры массива, на github, и я бы очень хотел чтобы получить комментарии по этому вопросу. Это позволяет вам сделать это:
// create unique_ptr to an array of 100 integers
auto a = make_unique<int[100]>();
// create a unique_ptr to an array of 100 integers and
// set the first three elements to 1,2,3
auto b = make_unique<int[100]>(1,2,3);