Cosa significa l'istruzione "return {}" in C ++ 11?


115

Cosa significa l'affermazione

return {};

in C ++ 11 indicare, e quando usarlo invece di (dire)

return NULL;

o

return nullptr;

59
restituisce un'istanza costruita in modo predefinito del tipo restituito dalla funzione.
Richard Hodges

Oppure è semplice return;senza valore?
i486

No, come rivela la discussione, è un errore in fase di compilazione se la tua funzione deve restituire qualcosa (cioè non di tipo restituito void) e scrivi solo return; D'altra parte return{};è valido se hai un tipo restituito.
Pedia

@Pedia Non sempre, alcuni oggetti richiederanno argomenti per la costruzione
MM

Risposte:


108

return {};indica "restituisce un oggetto del tipo restituito dalla funzione inizializzato con un inizializzatore di lista vuoto ". Il comportamento esatto dipende dal tipo di oggetto restituito.

Da cppreference.com (poiché l'OP è etichettato C ++ 11, ho escluso le regole in C ++ 14 e C ++ 17; fare riferimento al collegamento per ulteriori dettagli):

  • Se l'elenco di inizializzazione con parentesi graffe è vuoto e T è un tipo di classe con un costruttore predefinito, viene eseguita l'inizializzazione del valore.
  • Altrimenti, se T è un tipo aggregato, viene eseguita l'inizializzazione aggregata.
  • Altrimenti, se T è una specializzazione di std :: initializer_list, l'oggetto T viene inizializzato direttamente o inizializzato da copia, a seconda del contesto, dalla lista con parentesi graffe.
  • Altrimenti si considerano i costruttori di T, in due fasi:

    • Tutti i costruttori che accettano std :: initializer_list come unico argomento o come primo argomento se gli argomenti rimanenti hanno valori predefiniti, vengono esaminati e confrontati con la risoluzione dell'overload rispetto a un singolo argomento di tipo std :: initializer_list
    • Se la fase precedente non produce una corrispondenza, tutti i costruttori di T partecipano alla risoluzione dell'overload contro l'insieme di argomenti che consiste negli elementi della lista contro parentesi, con la restrizione che sono consentite solo conversioni non restrittive. Se questa fase produce un costruttore esplicito come la migliore corrispondenza per un'inizializzazione della lista di copia, la compilazione fallisce (nota, nella semplice inizializzazione della copia, i costruttori espliciti non vengono considerati affatto).
  • Altrimenti (se T non è un tipo di classe), se l'elenco con parentesi graffe ha un solo elemento e T non è un tipo di riferimento o è un tipo di riferimento compatibile con il tipo dell'elemento, T è direct- inizializzato (nell'inizializzazione dell'elenco diretto) o inizializzato dalla copia (nell'inizializzazione dell'elenco-copia), tranne per il fatto che non sono consentite conversioni di restringimento.

  • Altrimenti, se T è un tipo di riferimento non compatibile con il tipo dell'elemento. (questo fallisce se il riferimento è un riferimento non-const lvalue)
  • Altrimenti, se la lista con parentesi graffe non ha elementi, T viene inizializzato con valore.

Prima di C ++ 11, per una funzione che restituiva a std::string, avresti scritto:

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

Utilizzando la sintassi delle parentesi graffe in C ++ 11, non è necessario ripetere il tipo:

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

return NULLe return nullptrdovrebbe essere usato quando la funzione restituisce un tipo di puntatore:

any_type* get_pointer() {
    return nullptr;
}

Tuttavia, NULLè deprecato dal C ++ 11 perché è solo un alias per un valore intero (0), mentre nullptrè un vero tipo di puntatore:

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

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

91

Questo probabilmente crea confusione:

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

Questo probabilmente non è:

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

9
Quindi, è un errore in fase di compilazione se il tipo restituito non ha un costruttore predefinito, giusto?
Pedia

10
È un errore di compilazione se il tipo restituito è una classe che non dispone di un costruttore predefinito non esplicito e non è un aggregato.
Oktalist

3
Se il tipo ha un initializer_listcostruttore, non verrebbe usato se non è disponibile alcun costruttore predefinito?
celtschk

4
"probabilmente confuso"? È per questo che un'anima senza nome si è riferita a "Quell'oscenità gonfia che è C ++"? Qualunque risparmio di battiture che questo fornisce possa giustificare il potenziale di mancanza di chiarezza che offre? Questa è una domanda sincera. Per favore, convincimi con esempi pratici.
MickeyfAgain_BeforeExitOfSO

4
return {}NON è equivalente areturn SomeObjectWithADefaultConstructor{};
MM

26

return {};significa che {}è l'inizializzatore per il valore restituito . Il valore restituito viene inizializzato da un elenco con un elenco vuoto.


Di seguito sono riportate alcune informazioni di base sul valore restituito , basato su [stmt.return] nello standard C ++:

Per una funzione che restituisce per valore (cioè il tipo restituito non è un riferimento e non è un riferimento void), esiste un oggetto temporaneo chiamato valore restituito . Questo oggetto viene creato returndall'istruzione e i suoi inizializzatori dipendono da ciò che era nell'istruzione return.

Il valore restituito sopravvive fino alla fine dell'espressione completa nel codice che ha chiamato la funzione; se ha un tipo di classe, il suo distruttore verrà eseguito a meno che non abbia la durata estesa dal chiamante che associa un riferimento direttamente ad esso.

Il valore restituito può essere inizializzato in due modi diversi:

  • return some_expression;- il valore restituito è inizializzato da copiasome_expression
  • return { possibly_empty_list };- il valore restituito viene inizializzato dalla lista.

Supponendo che Tsia il tipo di ritorno della funzione, si noti che return T{};è diverso da return {}: nel primo, T{}viene creato un temporaneo , quindi il valore restituito viene inizializzato dalla copia da quel temporaneo.

Questo fallirà la compilazione se Tnon ha un costruttore di copia / spostamento accessibile, ma return {};avrà successo anche se quei costruttori non sono presenti. Di conseguenza, return T{};può mostrare effetti collaterali del costruttore di copie, ecc., Anche se questo è un contesto di elisione della copia, quindi potrebbe non esserlo.


Ecco un breve riepilogo dell'inizializzazione dell'elenco in C ++ 14 (N4140 [dcl.init.list] / 3), dove l'inizializzatore è un elenco vuoto:

  • Se Tè un aggregato, ogni membro viene inizializzato dal suo inizializzatore di parentesi graffa o uguale se ne aveva uno, altrimenti come se fosse {} (quindi applica questi passaggi ricorsivamente).
  • Se Tè un tipo di classe con un costruttore predefinito fornito dall'utente, viene chiamato tale costruttore.
  • Se Tè un tipo di classe con un = defaultcostruttore predefinito definito in modo implicito o ed, l'oggetto viene inizializzato con zero e quindi viene chiamato il costruttore predefinito.
  • Se Tè a std::initializer_list, il valore restituito è un tale elenco vuoto.
  • Altrimenti (cioè Tè un tipo non di classe - i tipi restituiti non possono essere array), il valore restituito è inizializzato con zero.

L'aggregato init viene prima e inizializza ricorsivamente ogni membro con {}, che può essere o meno valore-init.
TC

@TC esatto, sono passato da cppreference ma ho trascurato un "fino a C ++ 14"
MM

3

È una sorta di brevetto per una nuova istanza del tipo restituito dai metodi.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.