Что означает оператор return {} в C ++ 11?


115

Что означает заявление

return {};

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

return NULL;

или

return nullptr;

59
он возвращает созданный по умолчанию экземпляр возвращаемого типа функции.
Ричард Ходжес,

Или просто return;без значения?
i486

Нет, как показывает обсуждение, это ошибка времени компиляции, если ваша функция должна что-то возвращать (то есть не с типом возврата void), а вы пишете просто. return; С другой стороны, return{};это допустимо, если у вас есть возвращаемый тип.
Pedia,

@Pedia Не всегда, некоторые объекты требуют аргументов для построения
MM

Ответы:


108

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

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

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

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

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

До C ++ 11 для функции, возвращающей a std::string, вы должны были написать:

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

Используя синтаксис фигурных скобок в C ++ 11, вам не нужно повторять тип:

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

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

any_type* get_pointer() {
    return nullptr;
}

Однако NULLне рекомендуется, начиная с C ++ 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
}

91

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

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

Вероятно, это не так:

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

9
Итак, если у возвращаемого типа нет конструктора по умолчанию, это ошибка времени компиляции, верно?
Педия

10
Ошибка компиляции, если тип возвращаемого значения - это класс, который не имеет неявного конструктора по умолчанию и не является агрегатом.
Oktalist

3
Если у типа есть initializer_listконструктор, разве он не будет использоваться, если конструктор по умолчанию недоступен?
celtschk

4
"наверное сбивает с толку"? Не поэтому ли какая-то безымянная душа упомянула «эту раздутую непристойность, которая есть С ++»? Может ли любая экономия на нажатиях клавиш оправдать потенциальную нечеткость, которую он предлагает? Это искренний вопрос. Убедите меня практическими примерами.
MickeyfAgain_BeforeExitOfSO

4
return {}НЕ эквивалентноreturn SomeObjectWithADefaultConstructor{};
MM

26

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


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

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

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

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


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

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


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

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

Агрегатный init идет первым, и он рекурсивно инициализирует каждый член {}, который может быть или не быть значением-init.
TC

@TC правильно, я пошел по cppreference, но пропустил "до C ++ 14"
MM

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.