Co oznacza instrukcja „return {}” w C ++ 11?


115

Co oznacza oświadczenie

return {};

w C ++ 11 wskaż i kiedy go użyć zamiast (powiedzmy)

return NULL;

lub

return nullptr;

59
zwraca domyślnie skonstruowaną instancję zwracanego typu funkcji.
— Richard Hodges,

Czy jest to proste return;bez wartości?
— i486

Nie, jak pokazuje dyskusja, jest to błąd w czasie kompilacji, jeśli twoja funkcja powinna coś zwrócić (tj. Nie należy do typu zwracanego void), a ty napiszesz tylko. return; Z drugiej strony return{};jest to poprawne, jeśli masz typ zwracany.
— Pedia

@Pedia Nie zawsze, niektóre obiekty będą wymagały argumentów do skonstruowania
— MM

Odpowiedzi:


108

return {};wskazuje „zwraca obiekt typu zwracanego funkcji zainicjowany pustym inicjatorem listy ”. Dokładne zachowanie zależy od typu zwracanego obiektu.

Z cppreference.com (ponieważ OP jest oznaczony C ++ 11, wykluczyłem reguły w C ++ 14 i C ++ 17; zapoznaj się z linkiem po dalsze szczegóły):

  • Jeśli lista braced-init-list jest pusta, a T jest typem klasy z domyślnym konstruktorem, wykonywana jest inicjalizacja wartości.
  • W przeciwnym razie, jeśli T jest typem agregującym, wykonywana jest inicjalizacja agregacji.
  • W przeciwnym razie, jeśli T jest specjalizacją std :: initializer_list, obiekt T jest inicjowany bezpośrednio lub kopiowany, w zależności od kontekstu, z listy braced-init-list.
  • W przeciwnym razie konstruktory T są rozważane w dwóch fazach:

    • Wszystkie konstruktory, które przyjmują std :: initializer_list jako jedyny argument lub jako pierwszy argument, jeśli pozostałe argumenty mają wartości domyślne, są sprawdzane i dopasowywane przez rozpoznawanie przeciążenia względem pojedynczego argumentu typu std :: initializer_list
    • Jeśli poprzedni etap nie daje dopasowania, wszystkie konstruktory T uczestniczą w rozwiązywaniu przeciążenia względem zestawu argumentów, który składa się z elementów listy braced-init-list, z zastrzeżeniem, że dozwolone są tylko konwersje nie zawężające. Jeśli na tym etapie zostanie utworzony jawny konstruktor jako najlepsze dopasowanie do inicjalizacji listy kopii, kompilacja nie powiedzie się (uwaga, w prostej inicjalizacji kopiowania jawne konstruktory nie są w ogóle uwzględniane).
  • W przeciwnym razie (jeśli T nie jest typem klasy), jeśli lista stężonych-init ma tylko jeden element i albo T nie jest typem referencyjnym, albo jest typem referencyjnym zgodnym z typem elementu, T jest bezpośrednie- zainicjowany (w inicjalizacji bezpośredniej listy) lub zainicjowany przez kopię (w inicjalizacji listy kopii), z wyjątkiem tego, że konwersje zawężające są niedozwolone.

  • W przeciwnym razie, jeśli T jest typem referencyjnym, który nie jest zgodny z typem elementu. (kończy się niepowodzeniem, jeśli odniesienie jest odniesieniem innym niż stała lwartość)
  • W przeciwnym razie, jeśli lista-init-braced-init nie zawiera elementów, T jest inicjalizowana wartością.

Przed C ++ 11, dla funkcji zwracającej a std::string, napisałbyś:

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

Używając składni nawiasów klamrowych w C ++ 11, nie musisz powtarzać typu:

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

return NULLi return nullptrpowinno być używane, gdy funkcja zwraca typ wskaźnika:

any_type* get_pointer() {
    return nullptr;
}

Jednak NULLjest przestarzały od C ++ 11, ponieważ jest tylko aliasem do wartości całkowitej (0), podczas gdy nullptrjest prawdziwym typem wskaźnika:

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

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

91

Jest to prawdopodobnie mylące:

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

To prawdopodobnie nie jest:

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

9
Tak więc jest to błąd kompilacji, jeśli typ zwracany nie ma domyślnego konstruktora, prawda?
— Pedia

10
Jest to błąd kompilacji, jeśli typ zwracany jest klasą, która nie ma jawnego konstruktora domyślnego i nie jest agregacją.
— Oktalist

3
Jeśli typ ma initializer_listkonstruktora, czy nie zostanie on użyty, jeśli nie jest dostępny domyślny konstruktor?
— celtschk

4
„prawdopodobnie zagmatwany”? Czy to dlatego jakaś nienazwana dusza odniosła się do „tej nadętej nieprzyzwoitości, jaką jest C ++”? Czy jakiekolwiek oszczędności związane z naciśnięciami klawiszy mogą uzasadniać potencjalny brak przejrzystości, jaki oferuje? To jest szczere pytanie. Proszę, przekonaj mnie praktycznymi przykładami.
— MickeyfAgain_BeforeExitOfSO

4
return {}NIE jest odpowiednikiemreturn SomeObjectWithADefaultConstructor{};
— MM

26

return {};oznacza, że {}jest to inicjator wartości zwracanej . Wartość zwracana jest inicjowana listą z pustą listą.


Oto kilka informacji na temat zwracanej wartości , na podstawie [stmt.return] w standardzie C ++:

W przypadku funkcji, która zwraca wartość (tj. Typ zwracany nie jest odwołaniem i nie jest void), istnieje obiekt tymczasowy zwany wartością zwracaną . Ten obiekt jest tworzony przez returninstrukcję, a jego inicjatory zależą od tego, co było w instrukcji return.

Wartość zwracana zachowuje ważność do końca pełnego wyrażenia w kodzie, który wywołał funkcję; jeśli ma typ klasy, to jego destruktor będzie działał, chyba że jego żywotność zostanie przedłużona przez obiekt wywołujący wiążący bezpośrednio z nim odwołanie.

Wartość zwracaną można zainicjować na dwa różne sposoby:

  • return some_expression;- wartość zwracana jest inicjowana przez kopiowanie zsome_expression
  • return { possibly_empty_list };- wartość zwracana jest inicjalizowana listą z listy.

Zakładając, że Tjest to typ zwracany przez funkcję, zwróć uwagę, że return T{};jest inny niż return {}: w pierwszym przypadku T{}tworzony jest tymczasowy , a następnie zwracana wartość jest kopiowana z tego tymczasowego.

Nie uda się to skompilować, jeśli Tnie ma dostępnego konstruktora kopiowania / przenoszenia, ale return {};powiedzie się, nawet jeśli te konstruktory nie są obecne. W związku z tym return T{};może pokazywać efekty uboczne konstruktora kopiującego itp., Chociaż jest to kontekst elekcji kopiowania, więc może nie.


Oto krótkie podsumowanie inicjalizacji listy w C ++ 14 (N4140 [dcl.init.list] / 3), gdzie inicjalizatorem jest pusta lista:

  • Jeśli Tjest agregacją, każdy element członkowski jest inicjowany z jego inicjatora nawiasu klamrowego lub równego, jeśli taki miał, w przeciwnym razie tak, jakby był przez {} (więc zastosuj te kroki rekurencyjnie).
  • Jeśli Tjest typem klasy z domyślnym konstruktorem dostarczonym przez użytkownika, wywoływany jest ten konstruktor.
  • Jeśli Tjest typem klasy z niejawnie zdefiniowanym lub = defaultdomyślnym konstruktorem ed, obiekt jest inicjowany zerem, a następnie wywoływany jest konstruktor domyślny.
  • Jeśli Tjest a std::initializer_list, to zwracana wartość jest pustą taką listą.
  • W przeciwnym razie (tj. Nie Tjest typem klasowym - zwracane typy nie mogą być tablicami), wartość zwracana jest inicjowana przez zero.

Agregacja init jest na pierwszym miejscu i rekurencyjnie inicjuje każdy element członkowski {}, co może, ale nie musi, być wartością init.
— TC

@TC dobrze, wybrałem cppreference, ale przeoczyłem „do C ++ 14”
— MM

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.