Является ли make_unique в списке инициализатора в конструкторе копирования хорошей целью не использовать спецификатор noexcept? - PullRequest
0 голосов
/ 25 января 2019

У меня есть препятствие с указателем noexcept рядом с моим конструктором копирования.

#include <memory>
#include <vector>

class Foo final {
 public:
  Foo() noexcept = default;
  Foo(const Foo& oth) : impl_(std::make_unique<Foo::Impl>()) {} // <---
  ~Foo() noexcept = default;

private:
  class Impl;
  std::unique_ptr<Impl> impl_;
};

class Foo::Impl {
  ...
 private:
  std::vector<int> some_data;
}

Я не уверен, стоит ли ставить noexcept рядом с конструктором копирования, пока есть std::make_unique, который может выдатьbad_alloc.

Любая помощь будет оценена!

Ответы [ 5 ]

0 голосов
/ 25 января 2019

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

РЕДАКТИРОВАТЬ: я рекомендую вам прочитать пункт 14 Скотта Мейерса "Эффективный Современный C ++, он хорошо описывает преимущества использования noexcept и когда его использовать.

0 голосов
/ 25 января 2019

Рекомендации по кодированию cpp довольно ясны в E.12: Использовать noexcept при выходе из функции из-за того, что бросок невозможен или недопустим

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

Пример из руководства:

vector<double> munge(const vector<double>& v) noexcept
{
    vector<double> v2(v.size());
    // ... do something ...
}

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

Таким образом, если неудачная конструкция Foo может быть обработана с использованием блока try-catch без серьезных проблем. Тогда вы не будете использовать noexcept там.

0 голосов
/ 25 января 2019

Простой ответ : Не объявляйте его, кроме как, если вы знаете, что он может выдать, и у вас нет действительно веской причины для этого (например, вы хотите, чтобы ваше приложение вызывало std :: terminateесли копия не может быть сделана).

Спросите себя, какова будет прибыль.Может ли компилятор оптимизировать что-нибудь, когда это не исключение?Я не вижу много случаев, когда это имело бы место (наиболее распространенный случай, когда я вижу оптимизацию для перемещения, так как тогда контейнеры библиотеки std могут использовать его - они проверяют, не является ли операция перемещения исключением, поэтому они могут сохранить свои гарантии).Другое место, где вы можете его использовать, - это когда вы хотите задокументировать, что функция не может выбросить.Это совершенно не так.

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

Итак, летом вы ничего не получаете, но можете что-то потерять.

Также ознакомьтесь с основными рекомендациями:

E.12:Используйте noexcept при выходе из функции из-за того, что бросок невозможен или недопустим

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

  • она не сгенерирует или
  • , то вас не волнует случай исключения.Вы готовы завершить работу программы, потому что не можете обработать исключение, такое как std :: bad_alloc, из-за исчерпания памяти.

Не рекомендуется создавать исключение, если вы являетесь прямым владельцемобъект.

Кстати, следующие специальные функции-члены неявно noexcept:

  • Конструктор по умолчанию
  • Деструктор (насколько я знаю, дажеесли вы явно добавите его)
  • Переместите и скопируйте конструктор
  • Переместите и скопируйте оператор присваивания
0 голосов
/ 25 января 2019

Когда использовать noexcept

"лучшие практики"

Необходимо подумать о том, нужно ли мне добавлять noexcept после того, как каждое объявление функции значительно уменьшит производительность программиста (и, честно говоря, это будет боль).

Хорошо, тогда используйте его, когда очевидно, что функция никогда не сработает.

Когда можно реально ожидать улучшения производительности после использования noexcept? [...] Лично я забочусь о noexcept из-за расширенной свободы, предоставленной компилятору для безопасного применения определенных видов оптимизаций.

Похоже, что наибольшую выгоду от оптимизации получают пользовательские оптимизации, а не компиляторы из-за возможности проверки noexcept и перегрузки на нем. Большинство компиляторов следуют методу обработки исключений без штрафов, если вы не выбросили, поэтому я сомневаюсь, что это сильно изменит (или что-нибудь) уровень машинного кода в вашем коде, хотя, возможно, уменьшит размер двоичного кода, удалив обработку код.

Использование noexcept в больших 4 (конструкторы, присваивания, а не деструкторы, поскольку они уже noexcept), вероятно, приведет к лучшим улучшениям, поскольку noexcept проверки являются «общими» в коде шаблона, например в контейнерах std , Например, std::vector не будет использовать движение вашего класса, если оно не помечено noexcept (или компилятор может вывести его иначе).

0 голосов
/ 25 января 2019

Я считаю, что это действительно зависит от контекста. Если вы можете разумно обработать это исключение (и ожидать, что оно будет сгенерировано), то вам не следует помечать его как noexcept. С другой стороны, если ваша программа не может восстановиться после исключения, вы также можете пометить его как noexcept.
Я бы сказал, что второй случай верен, когда объект для выделения довольно мал (вы не сможете выполнить большую обработку исключений, если не сможете выделить один char). Первый должен происходить для действительно больших объектов (тогда вы можете отложить размещение, например). При этом, если вы копируете огромный объект ... Почему бы не переместить его вместо этого?

...