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


115

Що означає твердження

return {};

в C ++ 11 вкажіть, а коли використовувати його замість (скажіть)

return NULL;

або

return nullptr;

59
він повертає створений за замовчуванням екземпляр типу повернення функції.
Річард Ходжес

Або просто return;без вартості?
i486

Ні, як показує дискусія, це помилка часу компіляції, якщо ваша функція повинна повертати щось (тобто не недійсний тип повернення), а ви пишете просто return; З іншого боку return{};, дійсне, якщо у вас є тип повернення.
Педіа

@ Педія Не завжди для побудови якогось об'єкта знадобляться аргументи
ММ

Відповіді:


108

return {};вказує "повернути об'єкт типу повернення функції, ініціалізований порожнім списком-ініціалізатором ". Точна поведінка залежить від типу повернутого об'єкта.

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

  • Якщо список braced-init порожній і T - тип класу з конструктором за замовчуванням, виконується ініціалізація значень.
  • В іншому випадку, якщо T - агрегатний тип, виконується сукупна ініціалізація.
  • В іншому випадку, якщо T - спеціалізація std :: inicijalizer_list, об'єкт T прямо-ініціалізується або ініціалізується копією, залежно від контексту, зі списку, закресленого braced-init
  • В іншому випадку конструктори T розглядаються у дві фази:

    • Усі конструктори, які приймають std :: inicijalizer_list як єдиний аргумент, або як перший аргумент, якщо в інших аргументах є значення за замовчуванням, досліджуються та узгоджуються за допомогою роздільної здатності перевантаження проти одного аргументу типу std :: inicijalizer_list
    • Якщо попередній етап не відповідає збігу, всі конструктори T беруть участь у вирішенні перевантаження проти набору аргументів, що складається з елементів списку braced-init, з обмеженням, що дозволено лише перетворення, що не звужує. Якщо цей етап створює явний конструктор як найкращий збіг для ініціалізації копіювання-списку, компіляція не вдається (зауважте, у простому ініціалізації копіювання явні конструктори взагалі не розглядаються).
  • В іншому випадку (якщо T не є типом класу), якщо в braced-init-list є лише один елемент, або T не є еталонним типом, або є еталонним типом, сумісним із типом елемента, T є прямим- ініціалізована (у прямому списку-ініціалізація) або копійована ініціалізована (при копіюванні-списку-ініціалізація), за винятком того, що звуження конверсій заборонено.

  • В іншому випадку, якщо T - це еталонний тип, який не сумісний з типом елемента. (це не вдається, якщо посилання є нецікавим посиланням на значення)
  • В іншому випадку, якщо у списку з впорядкованим списком немає елементів, 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
Це помилка компіляції, якщо тип повернення - це клас, який не має явного конструктора за замовчуванням і не є сукупністю.
Окталіст

3
Якщо тип має initializer_listконструктор, чи не буде він використаний, якщо конструктор за замовчуванням недоступний?
celtschk

4
"певно, заплутаний"? Це чомусь якась безіменна душа посилалася на "Та роздута непристойність, яка є С ++"? Чи може будь-яка економія при натисканні клавіш виправдати потенціал недостатньої ясності, яку він пропонує? Це щире питання. Будь ласка, переконайте мене на практичних прикладах.
MickeyfAgain_BeforeExitOfSO

4
return {}НЕ еквівалентнийreturn SomeObjectWithADefaultConstructor{};
ММ

26

return {};означає, що {}це ініціалізатор для повернутого значення . Повернене значення ініціюється list з порожнім списком.


Ось деякий фон щодо значення , що повертається , на основі [stmt.return] у стандарті C ++:

Для функції, яка повертається за значенням (тобто тип повернення не є посиланням і не void), існує тимчасовий об'єкт, який називається значенням повернення . Цей об'єкт створюється returnоператором, і його ініціалізатори залежать від того, що було в операторі return.

Повернене значення зберігається до кінця повно вираження в коді, який викликав функцію; якщо він має тип класу, то його деструктор запуститься, якщо його життя не буде продовжено, якщо абонент пов'язує посилання безпосередньо на нього.

Повернене значення може бути ініціалізовано двома різними способами:


Якщо припустити T, що це тип повернення функції, то зауважте, що return T{};відрізняється від return {}: у першому створюється тимчасове T{}, а потім повернене значення копіюється ініціалізується з цього тимчасового.

Це не вдасться скомпілювати, якщо Tне має доступної копії / переміщення-конструктора, але return {};буде успішною, навіть якщо цих конструкторів немає. Відповідно, return T{};можуть проявлятися побічні ефекти конструктора копіювання тощо, хоча це контекст елісії копіювання, тому він не може.


Ось короткий підсумок ініціалізації списку в C ++ 14 (N4140 [dcl.init.list] / 3), де ініціалізатор - порожній список:

  • Якщо Tце сукупність, то кожен член ініціалізується зі свого ідентифікатора дужок або рівних, якщо він був, інакше, як якщо б {} (тому застосовуйте ці кроки рекурсивно).
  • Якщо Tце клас класу з наданим користувачем конструктором за замовчуванням, цей конструктор викликається.
  • Якщо Tце клас класу з неявно визначеним = defaultконструктором або редактором за замовчуванням, об'єкт ініціалізується нулем і тоді викликається конструктор за замовчуванням.
  • Якщо Tце a std::initializer_list, повернене значення - порожній такий список.
  • В іншому випадку (тобто Tнекласовий тип - типи повернення не можуть бути масивами), значення повернення ініціалізується нулем.

Агрегатна init виходить першою, і вона рекурсивно ініціалізує кожного члена {}, який може бути, а може і не бути значенням init.
ТК

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.