Что означает выражение "return {}" в С++ 11? - программирование
Подтвердить что ты не робот

Что означает выражение "return {}" в С++ 11?

Что делает оператор

return {};

в С++ 11 указывает, а когда использовать его вместо (скажем)

return NULL;

или

return nullptr;
4b9b3361

Ответ 1

return {}; указывает: "Возвращает объект возвращаемого типа функции, инициализированный пустым list-initializer". Точное поведение зависит от типа возвращаемого объекта.

Из cppreference.com (потому что OP отмечен С++ 11, я исключил правила в С++ 14 и С++ 17, см. ссылка для получения дополнительной информации):

  • Если список braced-init пуст, а T - тип класса со стандартным конструктором, выполняется инициализация значения.
  • В противном случае, если T является агрегатным типом, выполняется агрегатная инициализация.
  • В противном случае, если T является специализацией std:: initializer_list, объект T инициализируется с прямой инициализацией или копируется в зависимости от контекста из списка braced-init-list.
  • В противном случае конструкторы T рассматриваются в двух фазах:

    • Все конструкторы, которые принимают std:: initializer_list как единственный аргумент, или как первый аргумент, если остальные аргументы имеют значения по умолчанию, проверяются и сопоставляются разрешением перегрузки по одному аргументу типа std:: initializer_list
    • Если предыдущий этап не дает соответствия, все конструкторы T участвуют в разрешении перегрузки против множества аргументов, которые состоят из элементов списка с привязкой-инициализацией, с ограничением, допускающим только не суживающиеся преобразования, Если этот этап создает явный конструктор как наилучшее совпадение для инициализации списка копий, компиляция не выполняется (обратите внимание, что при простой инициализации копии явные конструкторы вообще не рассматриваются).
  • В противном случае (если T не является типом класса), если бит-init-list имеет только один элемент и либо T не является ссылочным типом, либо является ссылочным типом, который совместим с типом элемент, T является прямым инициализацией (в инициализации прямого списка) или инициализируется с копией (в инициализации списка копий), за исключением того, что сужение конверсий не допускается.

  • В противном случае, если T является ссылочным типом, который несовместим с типом элемента. (это не удается, если ссылка является ссылкой на константу без ссылки)
  • В противном случае, если в файле braced-init-list нет элементов, T инициализируется значением.

Прежде чем С++ 11, для функции, возвращающей std::string, вы бы написали:

std::string get_string() {
    return std::string();
}

Используя синтаксис привязки в С++ 11, вам не нужно повторять тип:

std::string get_string() {
    return {}; // an empty string is returned
}

return NULL и return nullptr следует использовать, когда функция возвращает тип указателя:

any_type* get_pointer() {
    return nullptr;
}

Однако NULL устарел с С++ 11, потому что это просто псевдоним целочисленного значения (0), а nullptr - тип реального указателя:

int get_int() {
    return NULL; // will compile, NULL is an integer
}

int get_int() {
    return nullptr; // error: nullptr is not an integer
}

Ответ 2

Это, вероятно, сбивает с толку:

int foo()
{
  return {};   // honestly, just return 0 - it clearer
}

Возможно, это не так:

SomeObjectWithADefaultConstructor foo()
{
  return {};
  // equivalent to return SomeObjectWithADefaultConstructor {};
}

Ответ 3

return {}; означает, что {} является инициализатором возвращаемого значения. Возвращаемое значение инициализируется списком с пустым списком.


Вот некоторый фон для возвращаемого значения, основанный на [stmt.return] в стандарте С++:

Для функции, возвращающейся по значению (т.е. тип возврата не является ссылкой, а не void), существует временный объект, называемый возвращаемым значением. Этот объект создается оператором return, а его инициализаторы зависят от того, что было в операторе return.

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

Возвращаемое значение может быть инициализировано двумя различными способами:

  • return some_expression; - возвращаемое значение copy-initialized из some_expression
  • return { possibly_empty_list }; - возвращаемое значение list-initialized из списка.

Предполагая, что T является возвращаемым типом функции, тогда обратите внимание, что return T{}; отличается от return {}: в первом создается временная T{}, а затем возвращаемое значение инициализируется с помощью этого временного,

Это не скомпилируется, если T не имеет доступного экземпляра copy/move-constructor, но return {}; будет успешным, даже если эти конструкторы отсутствуют. Соответственно, return T{}; может показывать побочные эффекты конструктора копирования и т.д., Хотя это контекст копирования, поэтому он не может.


Вот краткое описание инициализации списка в С++ 14 (N4140 [dcl.init.list]/3), где инициализатор представляет собой пустой список:

  • Если T является агрегатом, то каждый член инициализируется из его элемента с выравниванием или равным-инициализатором, если он имеет один, иначе как если бы он был {} (поэтому примените эти шаги рекурсивно).
  • Если T - это тип класса с предоставленным пользователем конструктором по умолчанию, этот конструктор вызывается.
  • Если T - это тип класса с неявным образом или конструктор по умолчанию = default ed, объект с нулевой инициализацией, а затем конструктор по умолчанию вызывается.
  • Если T является std::initializer_list, возвращаемое значение является пустым таким списком.
  • В противном случае (т.е. T является неклассовым типом - типы возврата не могут быть массивами), возвращаемое значение инициализируется нулем.

Ответ 4

Это своего рода короткая рука для нового экземпляра возвращаемого типа методов.