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;
Pedia
źródło
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
}
rgmt
źródło
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 {};
}
Richarda Hodgesa
źródło
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.
MM
źródło
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
3

To coś w rodzaju krótkiej ręki dla nowej instancji zwracanych metod.

Victor Mwenda
źródło