Зачем использовать std :: make_unique в С++ 17?
Насколько я понимаю, С++ 14 ввел std::make_unique
потому что из-за того, что не был указан порядок оценки параметров, это было небезопасно:
f(std::unique_ptr<MyClass>(new MyClass(param)), g()); // Syntax A
(Объяснение: если оценка сначала выделяет память для необработанного указателя, затем вызывает g()
и перед конструкцией std::unique_ptr
генерируется исключение, то память просачивается.)
Вызов std::make_unique
был способом ограничения порядка вызовов, таким образом делая вещи безопасными:
f(std::make_unique<MyClass>(param), g()); // Syntax B
С тех пор, С++ 17 разъяснили порядок оценки, что делает Синтаксис безопасными тоже, так что здесь мой вопрос: есть ли еще причина использовать std::make_unique
над std::unique_ptr
конструктора в С++ 17? Можете привести несколько примеров?
На данный момент единственная причина, которую я могу себе представить, заключается в том, что он позволяет набирать MyClass
только один раз (при условии, что вам не нужно полагаться на полиморфизм с помощью std::unique_ptr<Base>(new Derived(param))
). Однако это кажется довольно слабой причиной, особенно когда std::make_unique
не позволяет указывать удалитель, в то время как конструктор std::unique_ptr
делает.
И, чтобы быть ясным, я не сторонник удаления std::make_unique
из стандартной библиотеки (сохраняя это имеет смысл по крайней мере для обратной совместимости), а скорее задаюсь вопросом, существуют ли еще ситуации, в которых настоятельно рекомендуется std::unique_ptr
Ответы
Ответ 1
Вы правы в том, что основная причина была устранена. Есть все еще не использовать новые руководящие принципы и что это меньше печатать причины (не нужно повторять тип или использовать слово new
). По общему признанию, это не сильные аргументы, но мне действительно не нравится видеть new
в моем коде.
Также не забывайте о последовательности. Вы обязательно должны использовать make_shared
поэтому использование make_unique
является естественным и соответствует шаблону. Затем тривиально заменить std::make_unique<MyClass>(param)
на std::make_shared<MyClass>(param)
(или наоборот), где синтаксис A требует гораздо больше переписывания.
Ответ 2
make_unique
отличает T
от T[]
и T[N]
, unique_ptr(new...)
- нет.
Вы можете легко получить неопределенное поведение, передав новый указатель new[]
ed в unique_ptr<T>
или передав указатель, который был new
ed, в unique_ptr<T[]>
.
Ответ 3
Причина в том, чтобы иметь более короткий код без дубликатов. сравнить
f(std::unique_ptr<MyClass>(new MyClass(param)), g());
f(std::make_unique<MyClass>(param), g());
Вы сохраняете MyClass
, new
и брекеты. Марка стоит только на один символ больше по сравнению с ptr.
Ответ 4
Каждое использование new
должно быть особенно тщательно проверено для правильности жизни; это удаляется? Только однажды?
Каждое использование make_unique
не для этих дополнительных характеристик; пока владелец объекта имеет "правильное" время жизни, он рекурсивно заставляет уникальный указатель иметь "правильный".
Теперь верно, что unique_ptr<Foo>(new Foo())
во всех отношениях идентичен 1 для make_unique<Foo>()
; это просто требует более простой "grep вашего исходного кода для всех видов использования new
для их аудита".
1 на самом деле ложь в общем случае. Идеальная пересылка не идеальна, {}
, init по умолчанию, массивы - все исключения.
Ответ 5
С тех пор С++ 17 уточнил порядок оценки, сделав синтаксис A также безопасным
Это действительно недостаточно хорошо. Использование недавно введенного технического условия в качестве гарантии безопасности - не очень надежная практика:
- Кто-то может скомпилировать этот код в С++ 14.
- Вы бы поощряли использование raw
new
в другом месте, например. скопировав свой пример.
- Как С.М. предполагает, что при наличии дублирования кода один тип может быть изменен без изменения другого.
- Какой-то автоматический рефакторинг IDE может переместить этот
new
в другое место (хорошо, конечно, шансов на это немного).
Как правило, было бы неплохо, чтобы ваш код был соответствующим/надежным/четко действующим, не прибегая к языковой иерархии, не обращая внимания на незначительные или неясные технические положения в стандарте.
(По сути, это тот же аргумент, который я привел здесь о порядке уничтожения кортежей.)