usar returning como argument c++ c++11 return return-value brace-initialization list-initialization

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



como usar return en c++ (4)

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

¿Qué significa la declaración

return {};

en C ++ 11 indicar, y cuándo usarlo en lugar de (por ejemplo)

return NULL;

o

return nullptr;


Esto es probablemente confuso:

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

Esto probablemente no sea:

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


return {}; indica "devolver un objeto del tipo de retorno de la función inicializado con una list-initializer vacía". El comportamiento exacto depende del tipo de objeto devuelto.

De list-initializer (debido a que el OP está etiquetado como C ++ 11, list-initializer las reglas en C ++ 14 y C ++ 17; consulte el enlace para obtener más detalles):

  • Si braced-init-list está vacío y T es un tipo de clase con un constructor predeterminado, se realiza la inicialización del 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 copia inicializado, dependiendo del contexto, de la lista de inicialización arriostrada.
  • De lo contrario, los constructores de T se consideran, en dos fases:

    • Todos los constructores que toman std :: initializer_list como único argumento, o como el primer argumento si los argumentos restantes tienen valores predeterminados, se examinan y se comparan por resolución de sobrecarga contra un único 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 arriostrada, con la restricción de que solo se permiten conversiones no limitantes. Si esta etapa produce un constructor explícito como la mejor coincidencia para una inicialización de lista de copia, 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-init-braced 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 direct-list-initialization) o copy-initialized (en copy-list-initialization), excepto que no se permiten conversiones de reducció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 no constante)
  • De lo contrario, si la lista de inicialización entre paréntesis no tiene elementos, T tiene un valor inicializado.

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 necesita repetir el tipo:

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

return NULL y return nullptr deben usarse cuando la función devuelve un tipo de puntero:

any_type* get_pointer() { return nullptr; }

Sin embargo, NULL está en desuso desde C ++ 11 porque es solo un alias a un valor entero (0), mientras que nullptr es 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 }


return {}; significa que {} es el inicializador para el 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 y no es void ), hay un objeto temporal llamado valor de retorno . Este objeto es creado por la declaración de return , y sus inicializadores dependen de lo que haya en la declaración de devolución.

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 el llamante haya extendido su vida útil vinculando una referencia directamente a él.

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

Suponiendo que T es el tipo de retorno de la función, observe que return T{}; es diferente a return {} : en el primero, se crea una T{} temporal, y luego el valor de retorno se inicializa desde ese temporal.

Esto no se compilará si T no 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 la lista en C ++ 14 (N4140 [dcl.init.list] / 3), donde el inicializador es una lista vacía:

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