Короткий ответ
Конструктор default
ed должен иметь ту же сгенерированную сборку, что и эквивалентный конструктор инициализатора при условии, что автор включает правильные состояния constexpr
и noexcept
.
Я подозреваю, что может быть более эффективным, имеется в виду тот факт, что в целом он будет генерировать более оптимальный код, чем эквивалентный код, созданный разработчиком, который упускает такие возможности, как inline
, constexpr
и noexcept
.
Длинный ответ
Важная особенность, которую выполняют конструкторы default
ed, заключается в том, что они интерпретируют и определяют правильный статус как для constexpr
, так и для noexcept
Это то, что многие разработчики C ++ не указывают или могут указать неправильно. Поскольку основные рекомендации предназначены как для новых, так и для старых разработчиков C ++, вероятно, поэтому и упоминается оптимизация.
Статусы constexpr
и noexcept
могут по-разному влиять на генерацию кода:
constexpr
конструкторы гарантируют, что вызовы конструктора из значений, полученных из константных выражений, также приведут к константному выражению. Это может позволить таким вещам, как static
значения, которые не являются постоянными, фактически не требовать вызова конструктора (например, не требуется статическая инициализация или блокировка). Примечание. это работает для типов, которые сами по себе не могут существовать в constexpr
контексте - пока constexpr
конструктор правильно сформирован.
noexcept
может обеспечить лучшую сборку потребляющего кода, поскольку компилятор может предполагать, что никаких исключений не может произойти (и, следовательно, не требуется никакого кода для разворачивания стека). Кроме того, такие служебные программы, как шаблоны, которые проверяют std::is_nothrow_constructible...
, могут генерировать более оптимальные пути кода.
Помимо этого, конструкторы default
ed, определенные в теле класса, также делают свои определения видимыми для вызывающего, что позволяет улучшить встраивание (что, опять же, в противном случае может быть упущенной возможностью для оптимизации).
Примеры в Основных рекомендациях не очень хорошо демонстрируют эту оптимизацию. Однако рассмотрим следующий пример, который иллюстрирует реалистичный пример, в котором default
ing может выиграть:
class Foo {
int a;
std::unique_ptr<int> b;
public:
Foo() : a{42}, b{nullptr}{}
};
В этом примере верно следующее:
- Конструкция
Foo{}
не постоянное выражение
- Строительство
Foo{}
не noexcept
Сравните это с:
class Foo {
int a = 42;
std::unique_ptr<int> b = nullptr;
public:
Foo() = default;
};
На первый взгляд, это то же самое. Но внезапно теперь меняется следующее:
Foo{}
равно constexpr
, поскольку std::nullptr_t
конструктор constexpr
(хотя std::unique_ptr
не может использоваться в полном постоянном выражении)
Foo{}
- это noexcept
выражение
Вы можете сравнить созданную сборку с этим живым примером. Обратите внимание, что в случае default
не требуется никаких инструкций для инициализации foo
; вместо этого он просто назначает значения как константы через директиву компилятора (даже если значение не является постоянным).
Конечно, это тоже можно было написать:
class Foo {
int a;
std::unique_ptr<int> b;
public:
constexpr Foo() noexcept :a{42}, b{nullptr};
};
Однако для этого требуется предварительное знание того, что Foo
может быть одновременно constexpr
и noexcept
. Неправильный ответ может привести к проблемам. Что еще хуже, по мере развития кода состояние _39 _ / _ 40_ может стать некорректным - и это то, что default
конструктор обнаружил бы.
Использование default
также имеет дополнительное преимущество, заключающееся в том, что по мере развития кода оно может добавлять _43 _ / _ 44_ там, где это становится возможным, например, когда стандартная библиотека добавляет дополнительную поддержку constexpr
. Последний пункт - это то, что в противном случае выполнялось бы вручную каждый раз, когда автор меняет код.
Мелочь
Если вы откажетесь от использования инициализаторов-членов класса, то стоит упомянуть еще один важный момент: в коде нет способа достичь тривиальности, если он не генерируется компилятором (например, через конструкторы default
ed).
class Bar {
int a;
public:
Bar() = default; // Bar{} is trivial!
};
Тривиальность предлагает совершенно иное направление потенциальных оптимизаций, поскольку тривиальный конструктор по умолчанию не требует никаких действий со стороны компилятора. Это позволяет компилятору полностью опускать Bar{}
, если он видит, что объект позже перезаписывается.
person
Human-Compiler
schedule
23.01.2021