¿Qué significa la instrucción "return {}" en C ++ 11?


115

¿Qué dice la declaración

return {};

en C ++ 11 indique, y cuándo usarlo en lugar de (decir)

return NULL;

o

return nullptr;

59
devuelve una instancia construida por defecto del tipo de retorno de la función.
Richard Hodges

¿O es simple return;sin valor?
i486

No, como revela la discusión, es un error en tiempo de compilación si su función debe devolver algo (es decir, no de tipo de retorno nulo) y usted escribe simplemente. return; Por otro lado, return{};es válido si tiene un tipo de retorno.
Pedia

@Pedia No siempre, algunos objetos requerirán argumentos para construir
MM

Respuestas:


108

return {};indica "devolver un objeto del tipo de retorno de la función inicializado con un inicializador de lista vacío ". El comportamiento exacto depende del tipo de objeto devuelto.

De cppreference.com (debido a que el OP está etiquetado como C ++ 11, excluí las reglas en C ++ 14 y C ++ 17; consulte el enlace para obtener más detalles):

  • Si la lista de inicialización entre llaves está vacía y T es un tipo de clase con un constructor predeterminado, se realiza la inicialización de valor.
  • De lo contrario, si T es un tipo agregado, se realiza la inicialización agregada.
  • De lo contrario, si T es una especialización de std :: initializer_list, el objeto T se inicializa directamente o se inicializa con copia, según el contexto, desde la lista de inicialización entre llaves.
  • En caso contrario, se consideran los constructores de T, en dos fases:

    • Todos los constructores que toman std :: initializer_list como único argumento, o como primer argumento si los argumentos restantes tienen valores predeterminados, son examinados y comparados por resolución de sobrecarga con un solo argumento de tipo std :: initializer_list
    • Si la etapa anterior no produce una coincidencia, todos los constructores de T participan en la resolución de sobrecarga contra el conjunto de argumentos que consta de los elementos de la lista de inicialización con llaves, con la restricción de que solo se permiten conversiones sin restricción. Si esta etapa produce un constructor explícito como la mejor coincidencia para una inicialización de lista de copias, la compilación falla (nota, en la inicialización de copia simple, los constructores explícitos no se consideran en absoluto).
  • De lo contrario (si T no es un tipo de clase), si la lista de inicialización entre llaves tiene solo un elemento y T no es un tipo de referencia o es un tipo de referencia que es compatible con el tipo del elemento, T es directo- inicializado (en inicialización de lista directa) o inicializado con copia (en inicialización de lista de copia), excepto que no se permiten conversiones de restricción.

  • De lo contrario, si T es un tipo de referencia que no es compatible con el tipo del elemento. (esto falla si la referencia es una referencia de valor l no constante)
  • De lo contrario, si la lista de inicialización entre llaves no tiene elementos, T se inicializa con valor.

Antes de C ++ 11, para una función que devuelve un std::string, habría escrito:

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

Usando la sintaxis de llaves en C ++ 11, no es necesario repetir el tipo:

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

return NULLy return nullptrdebe usarse cuando la función devuelve un tipo de puntero:

any_type* get_pointer() {
    return nullptr;
}

Sin embargo, NULLestá en desuso desde C ++ 11 porque es solo un alias para un valor entero (0), mientras que nullptres un tipo de puntero real:

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

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

91

Probablemente esto sea confuso:

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

Probablemente esto no sea:

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

9
Entonces, es un error en tiempo de compilación si el tipo de retorno no tiene un constructor predeterminado, ¿correcto?
Pedia

10
Es un error de compilación si el tipo de retorno es una clase que no tiene un constructor predeterminado no explícito y no es un agregado.
Oktalist

3
Si el tipo tiene un initializer_listconstructor, ¿no se usaría si no hay un constructor predeterminado disponible?
celtschk

4
"probablemente confuso"? ¿Es por eso que algún alma anónima se refirió a "Esa obscenidad hinchada que es C ++"? ¿Puede cualquier ahorro en las pulsaciones de teclas que esto proporcione justificar el potencial de falta de claridad que ofrece? Esta es una pregunta sincera. Convénceme con ejemplos prácticos.
MickeyfAgain_BeforeSalirOfSO

4
return {}NO es equivalente areturn SomeObjectWithADefaultConstructor{};
MM

26

return {};significa que {}es el inicializador del valor de retorno . El valor de retorno se inicializa con una lista vacía.


Aquí hay algunos antecedentes sobre el valor de retorno , basado en [stmt.return] en el estándar C ++:

Para una función que devuelve por valor (es decir, el tipo de retorno no es una referencia ni tampoco void), hay un objeto temporal llamado valor de retorno . Este objeto es creado por elreturn declaración y sus inicializadores dependen de lo que estaba en la declaración de retorno.

El valor de retorno sobrevive hasta el final de la expresión completa en el código que llamó a la función; si tiene un tipo de clase, entonces su destructor se ejecutará a menos que su vida útil se extienda por el llamador vinculando una referencia directamente a él.

El valor de retorno se puede inicializar de dos formas diferentes:

  • return some_expression;- el valor de retorno se copia-inicializa desdesome_expression
  • return { possibly_empty_list };- el valor de retorno se inicializa por lista de la lista.

Suponiendo que Tes el tipo de retorno de la función, tenga en cuenta que return T{};es diferente a return {}: en el primero, T{}se crea un temporal , y luego el valor de retorno se inicializa con copia a partir de ese temporal.

Esto no se podrá compilar si Tno tiene un constructor de copia / movimiento accesible, pero return {};tendrá éxito incluso si esos constructores no están presentes. En consecuencia, return T{};puede mostrar efectos secundarios del constructor de copia, etc., aunque este es un contexto de elisión de copia, por lo que puede que no.


Aquí hay un breve resumen de la inicialización de lista en C ++ 14 (N4140 [dcl.init.list] / 3), donde el inicializador es una lista vacía:

  • Si Tes un agregado, entonces cada miembro se inicializa a partir de su inicializador de llaves o igual si tuviera uno, de lo contrario, como por {} (así que aplique estos pasos de forma recursiva).
  • Si Tes un tipo de clase con un constructor predeterminado proporcionado por el usuario, ese constructor se llama.
  • Si Tes un tipo de clase con un = defaultconstructor predeterminado definido implícitamente o ed, el objeto se inicializa en cero y luego se llama al constructor predeterminado.
  • Si Tes a std::initializer_list, el valor de retorno es una lista de este tipo vacía.
  • De lo contrario ( Tes decir, es un tipo que no es de clase; los tipos de retorno no pueden ser matrices), el valor de retorno se inicializa en cero.

La inicialización agregada es lo primero, y de forma recursiva inicializa cada miembro con {}, que puede ser o no value-init.
TC

@TC correcto, fui por cppreference pero pasé por alto un "hasta C ++ 14"
MM

3

Es una especie de abreviatura para una nueva instancia del tipo de retorno de métodos.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.