c++ - son - Sintaxis más precisa para establecer el valor de argumento predeterminado para el constructor predeterminado
retorno de variables en c++ (2)
Sí:
void foo(a::really::long::type::name arg = {});
Para resumir las siguientes definiciones estándar:
Esta es la inicialización de la lista. Dependiendo del tipo, se realiza la inicialización agregada o el objeto se inicializa en valor, lo que a su vez implica inicialización predeterminada o cero inicializado.
Algunos casos de "esquina" son cuando el tipo es una especialización de std::initializer_list
o cuando el tipo tiene un constructor std::initializer_list
(se llama si no tiene un constructor predeterminado)
Las citas estándar relevantes (para encontrar definiciones):
§8.3.6 Argumentos predeterminados [dcl.fct.default]
1 Si se especifica una cláusula de inicialización en una declaración de parámetro, esta cláusula de inicialización se utiliza como argumento predeterminado
5 El argumento predeterminado tiene las mismas restricciones semánticas que el inicializador en una declaración de una variable del tipo de parámetro, utilizando la semántica de inicialización de copia (8.5)
§8.5.4 Inicialización de lista [dcl.init.list]
1 La inicialización de la lista es la inicialización de un objeto o referencia de una lista inicial arrinconada. Tal inicializador se llama una lista de inicializadores, [...]. Una lista de inicializadores puede estar vacía. La inicialización de la lista puede ocurrir en contextos de inicialización directa o copia de inicialización; [..] la inicialización de lista en un contexto de inicialización de copia se llama copia-lista-inicialización.
3 La inicialización de la lista de un objeto o referencia de tipo T se define de la siguiente manera:
- Si T es un agregado, se realiza la inicialización agregada (8.5.1)
- De lo contrario, si la lista de inicializadores no tiene elementos y T es un tipo de clase con un constructor predeterminado, el objeto se inicializa en valores .
- De lo contrario, si T es una especialización de std :: initializer_list, se construye un objeto prvalue initializer_list como se describe a continuación y se utiliza para inicializar el objeto según las reglas para la inicialización de un objeto de una clase del mismo tipo (8.5).
- De lo contrario, si T es un tipo de clase, se consideran constructores. Los constructores aplicables se enumeran y el mejor se elige a través de la resolución de sobrecarga (13.3, 13.3.1.7) [...]
- ...
- De lo contrario, si la lista de inicializadores no tiene elementos, el objeto se inicializa en valor .
§ 8.5 Inicializadores [dcl.init]
8 Para inicializar el valor de un objeto de tipo T significa:
- si T es un tipo de clase (posiblemente cv calificado) (Cláusula 9) con ningún constructor predeterminado (12.1) o un constructor predeterminado que es proporcionado por el usuario o eliminado, entonces el objeto se inicializa por defecto ;
- si T es un tipo de clase (posiblemente cv calificado) sin un constructor predeterminado proporcionado por el usuario o eliminado, entonces el objeto se inicializa en cero y se verifican las restricciones semánticas para la inicialización predeterminada, y si T tiene un constructor predeterminado no trivial , el objeto está inicializado por defecto ;
- si T es un tipo de matriz, entonces cada elemento se inicializa en valor ;
- de lo contrario, el objeto tiene cero inicialización
7 Para inicializar por defecto un objeto de tipo T significa:
- si T es un tipo de clase (posiblemente cv calificado) (Cláusula 9), se llama al constructor predeterminado (12.1) para T (y la inicialización está mal formada si T no tiene un constructor predeterminado o la resolución de sobrecarga (13.3) da como resultado ambigüedad o en una función que se elimina o inaccesible del contexto de la inicialización);
- si T es un tipo de matriz, cada elemento se inicializa por defecto ;
- de lo contrario, no se realiza ninguna inicialización.
6 Para inicializar a cero un objeto o referencia de tipo T significa:
- si T es un tipo escalar (3.9), el objeto se inicializa al valor obtenido convirtiendo el entero literal 0 (cero) en T;
- si T es un tipo de clase no sindicalizada (posiblemente cv calificado), cada miembro de datos no estático y cada subobjeto de clase base tiene cero inicialización y el relleno se inicializa a cero bits;
- si T es un tipo de unión (posiblemente cv calificado), el primer miembro de datos con nombre no estático del objeto se inicializa en cero y el relleno se inicializa a cero bits;
- si T es un tipo de matriz, cada elemento tiene cero inicialización;
- si T es un tipo de referencia, no se realiza ninguna inicialización.
§13.3.1.7 Inicialización por inicialización de lista [over.match.list]
1 Cuando los objetos del tipo de clase no agregado T se inicializan en la lista (8.5.4), la resolución de sobrecarga selecciona el constructor en dos fases:
- Inicialmente, las funciones candidatas son los constructores de listas de inicializadores (8.5.4) de la clase T y la lista de argumentos consta de la lista de inicializadores como un único argumento.
- Si no se encuentra un constructor viable de listas de inicializadores, la resolución de sobrecarga se realiza nuevamente, donde las funciones candidatas son todos los constructores de la clase T y la lista de argumentos consta de los elementos de la lista de inicializadores.
Si la lista de inicializadores no tiene elementos y T tiene un constructor predeterminado, la primera fase se omite. [...]
Uno podría querer declarar una función con un argumento y especificar que el valor predeterminado para el argumento es el resultado del constructor predeterminado del tipo:
void foo(a::really::long::type::name arg = a::really::long::type::name());
¿Hay una sintaxis más agradable para esto que no implique ingresar el nombre del tipo dos veces? Algo como:
void foo(a::really::long::type::name arg = default);
Me doy cuenta de que puedo escribir el nombre del tipo para hacerlo más bonito, pero tengo curiosidad de saber si existe tal sintaxis.
Un acercamiento peatonal es posible, si controlas la clase de arg
. Use un constructor de conversión sobrecargado para una enum
:
// Define this enum, and then write constructors which take dfl
enum dfl { dflval };
class a_really_long_type_name {
public:
a_really_long_type_name(dfl arg = dflval);
};
Ahora foo puede ser:
void foo(a_really_long_type_name arg = dflval);
Si puede aplicar esto, un beneficio es la portabilidad; esto debería funcionar bien en un compilador de C ++ de veinticinco años.
Varias clases pueden compartir esta dfl
enum
y su dflval
-flavored zero; es como tener una nueva palabra clave.
Debido a que una enum
es un tipo distinto, esto no interfiere con las sobrecargas del constructor para tipos enteros o caracteres, etc.
La desventaja es trabajar en algunas clases que ya tienen una construcción predeterminada provista por argumento predeterminado, lo que conduce a un código de constructor duplicado.