Declaración de función
Una declaración de función introduce el nombre de la función y su tipo. Una definición de función asocia el nombre/tipo de la función con el cuerpo de función.
Contenido |
[editar]Declaración de función
Las declaraciones de funciones pueden aparecer en cualquier ámbito. Una declaración de función en el ámbito de una clase introduce una función miembro de clase (a menos que se use el especificador friend), para más detalles véase funciones miembro y funciones amigas.
El tipo se función que se declara se compone del tipo de retorno (proporcionado por la secuencia-de-especificadores-de-declaración de la sintaxis de declaración) y el declarador de función.
noptr-declarador( lista-parámetros) cv(opcional)ref(opcional)excepción(opcional)atributos(opcional) | (1) | ||||||||
noptr-declarador( lista-parámetros) cv(opcional)ref(opcional)excepción(opcional)atributos(opcional)-> finalizador | (2) | (desde C++11) | |||||||
(vea Declaraciones para otros formatos de sintaxis de declarador)
auto
noptr-declarador | - | cualquier declarador válido, pero si comienza con *, &, o &&, tiene que estar entre paréntesis. | ||||||||
lista-parámetros | - | que puede estar vacía, lista separada por comas de parámetros de función (ver abajo para más detalles) | ||||||||
atributos | - | (desde C++11) lista opcional de atributos. Estos atributos se aplican al tipo de la función, no a la función en sí. Los atributos de la función aparecen después del identificador dentro del declarador y se combinan con los atributos que aparecen al principio de la declaración, si los hay. | ||||||||
cv | - | calificación constante/volátil, solo se permite en declaraciones de funciones miembro no estáticas. | ||||||||
ref | - | (desde C++11) calificación ref, solo se permite en declaraciones de funciones miembro no estáticas | ||||||||
excepción | - |
| ||||||||
finalizador | - | Tipo de retorno final, útil si el tipo de retorno depende de los nombres de argumentos, como en template<class T, class U>auto suma(T t, U u)-> decltype(t + u); o es complicado, como en auto fpif(int)->int(*)(int) |
Como se indica en Declaraciones, el declarador puede estar seguido de una cláusula requiere, que declara las restricticones asociadas a la función, que deben ser satisfechas para que la función pueda ser seleccionada por la resolución de sobrecarga. (ejemplo: void f1(int a) requires true;) Tenga en cuenta que la restricción asociada forma parte de la firma de la función, pero no forma parte del tipo de función. | (desde C++20) |
Los declaradores de función se pueden mezclar con otros declaradores, donde secuencia-de-especificadores-de-declaración permite:
// declara un int, un int*, una función, y un puntero a funciónint a =1, *p =NULL, f(), (*pf)(double);// la secuencia-de-especificadores-de-declaración es int// el declarador f() declarar (pero no define)// una función sin argumentos y tipo de retorno int struct S {virtualint f(char)const, g(int)&&;// declara dos funciones miembros no estáticasvirtualint f(char), x;// error de compilación: virtual (en la secuencia-de-especificadores-de-declaración)// solo se permite en la declaración de// funciones miembros no estáticas};
El uso de un tipo de objeto calificado volátil como tipo de parámetro o tipo de retorno está obsoleto. | (desde C++20) |
El tipo de retorno de una función no puede ser un tipo función o array (pero puede ser un puntero o referencia a ellos).
Como en cualquier declaración, los atributos que aparecen antes de la declaración y los que aparecen inmediatamente después del identificador en el declarador se aplican a la entidad declarada o definida (en este caso, a la función): [[noreturn]]void f [[noreturn]]();// correcto: ambos atributos de aplican a la función f Sin embargo, los atributos que aparecen después del declarador (en la sintaxis anterior), se aplican al tipo de la función, no a la función en si: void f()[[noreturn]];// error: este atributo no tiene efecto en la función misma | (desde C++11) |
Como toda declaración, el tipo de la función func
declarada como ret func(parámetros)
es ret(parámetros)
(excepto para la reescritura del tipo de parámetro que se describe a continuación): vea denominación de tipos.
Deducción de tipo devueltoSi la secuencia-de-especificación-de-declaración de la declaración de la función contiene la palabra clave int x =1;auto f(){return x;}// el tipo de retorno es intconstauto& f(){return x;}// el tipo de retorno es const int& Si el tipo de retorno es int x =1; decltype(auto) f(){return x;}// el tipo de retorno es int, como en decltype(x) decltype(auto) f(){return(x);}// el tipo de retorno es int&, como en decltype((x)) (nota: " Si hay múltiples sentencias de retorno, se debe deducir el mismo tipo en todas: auto f(bool val){if(val)return123;// se deduce el tipo de retorno intelsereturn3.14f;// error: se deduce el tipo de retorno float} Si no hay sentencia de retorno o si el argumento de la sentencia de retorno es una expresión vacía, el tipo de retorno declarado debe ser o bien decltype(auto), en cuyo caso el tipo deducido de retorno es void, o bien (que puede estar calificado cv) auto , en cuyo caso el tipo de retorno deducido es (con idéntica cualificación cv) void: auto f(){}// devuelve voidauto g(){return f();}// devuelve voidauto* x(){}// error: no puede deducir auto* desde void Una vez que se ve una sentencia de retorno en una función, el tipo de retorno deducido en esa sentencia se puede usar en el resto de la función, incluso en otras sentencias de retorno: auto suma(int i){if(i ==1)return i;// el tipo de retorno de suma es intelsereturn suma(i -1)+ i;// correcto: el tipo de retorno de suma ya se conoce} Si la sentencia de retorno usa una lista de inicialización entre paréntesis, no se permite la deducción: auto func (){return{1, 2, 3};}// error Las funciones virtualesy las corrutinas(desde C++20)no pueden usar la deducción de tipo de retorno: struct F {virtualauto f(){return2;}// error}; Si una función usa la deducción de tipo de retorno, no puede ser redeclarada usando el tipo deducido, u otro tipo de deducción de tipo de retorno aunque se deduzca el mismo tipo: auto f();// declarada, aún no definidaauto f(){return42;}// definida, el tipo de retorno es intint f();// error: no se puede usar el tipo deducido decltype(auto) f();// error: diferente forma de deducciónauto f();// correcot: re-declaración template<typename T>struct A {friend T frf(T);};auto frf(int i){return i;}// no una amiga de A<int> Las plantillas de función distintas de las funciones de conversión definidas por el usuario pueden usar el deducción del tipo de retorno. La deducción tiene lugar en la creación de instancias incluso si la expresión en la sentencia de retorno no es dependiente. Esta instanciación no se encuentra en un contexto inmediato para efectos de la SFINAE. template<class T>auto f(T t){return t;}typedef decltype(f(1)) fint_t;// instancia f<int> para deducir el tipo de retorno template<class T>auto f(T* t){return*t;}void g(){int(*p)(int*)=&f;}// instancias ambas f para determinar el tipo de retorno,// selecciona la segunda sobrecarga de la plantilla Las especializaciones de las plantillas de función que usan la deducción del tipo de retorno deben usar los mismos marcadores del tipo de valor de retorno: template<typename T>auto g(T t){return t;}// #1templateauto g(int);// correcto: tipo de retorno es int// template char g(char); // error: no coincide con plantilla template<>auto g(double);// correcto: declaración adelantada con tipo de retorno desconocidotemplate<typename T> T g(T t){return t;}// correcto: no equivalente a #1 templatechar g(char);// correcto: ahora hay una plantilla coincidentetemplateauto g(float);// coincide con #1// auto h() { return g(42); } // error: ambiguo Las declaraciones de creación de instancias explícitas no crean instancias de plantillas de funciones que usan la deducción del tipo de retorno: template<typename T>auto f(T t){return t;}externtemplateauto f(int);// no instancia f<int> int(*p)(int)= f;// crea una instancia de f<int> para determinar el tipo de retorno,// pero aún se requiere una definición de creación de// instancias explícita en algún lugar del programa | (desde C++14) |
[editar]Lista de parámetros
La lista de parámetros determina los argumentos que se pueden especificar cunado se llama a una función. Es una lista separada por comas de declaraciones de parámetros, cada uno de los cuales tiene la siguiente sintaxis:
atributos(opcional)secuencia-de-especificadores-de-delaracióndeclarador | (1) | ||||||||
atributos(opcional)secuencia-de-especificadores-de-delaracióndeclarador= inicializador | (2) | ||||||||
atributos(opcional)secuencia-de-especificadores-de-delaracióndeclarador-abstracto(opcional) | (3) | ||||||||
atributos(opcional)secuencia-de-especificadores-de-delaracióndeclarador-abstracto(opcional)= inicializador | (4) | ||||||||
void | (5) | ||||||||
int f(int a, int* p, int(*(*x)(double))[3]);
int f(int a =7, int* p = nullptr, int(*(*x)(double))[3]= nullptr);
int f(int, int*, int(*(*)(double))[3]);
int f(int=7, int*= nullptr, int(*(*)(double))[3]= nullptr);
void
(que puede estar cualificado cv) no se puede usar en una lista de parámetros: int f(void, int); y int f(constvoid); son errores (aunque se pueden usar tipos derivados como void*
). En una plantilla, solo se pueden usar tipo void no dependiente (una función que toma un único parámetro de tipo T
no puede llegar a ser una función sin parámetros si es instanciada con T = void
).Al final de la lista de parámetros puede aparecer una elipsis ...
; esto declara una función variádica:
int printf(constchar* fmt ...);
Por compatibilidad con C89, puede aparecer una como opcional antes de la elipsis si la lista de parámetros tiene al menos un parámetro:
int printf(constchar* fmt, ...);// correcto, lo mismo que arrriba
Aunque secuencia-de-especificadores-de-declaración implica que pueden existir especificadores distintos de ñps especificadores de tipo, el único otro especificador permitido es registery auto(hasta C++11), y no tiene ningún efecto. | (hasta C++17) |
Si cualquiera de los parámetros de la función usa un marcador de posición ( void f1(auto);// lo mismo que template<class T> void f(T)void f2(C1 auto);// lo mismo que template<C1 T> void f7(T), si C1 es un concepto | (desde C++20) |
Los nombres de parámetros declarados en declaraciones de funciones son normalmente para propósitos de documentación. Se usan (pero siguen siendo opcionales) en las definiciones de funciones.
El tipo de cada parámetro de función en la lista de parámetros se determina a partir de las siguientes reglas:
Debido a estas reglas, las siguientes declaraciones de función declaran exactamente la misma función:
int f(char s[3]);int f(char[]);int f(char* s);int f(char*const);int f(char*volatile s);
Las siguientes declaraciones también declaran exactamente la misma función:
int f(int());int f(int(*g)());
El tipo de parámetro no puede ser un tipo de que incluya una referencia o un puntero a un array de límites desconocidos, incluidos punteros/array de niveles múltiples de dichos tipos, o un puntero a funciones cuyos parámetros sean de dichos tipos.
La elipsis que indica argumentos variádicos no necesita estar precedida por una coma, incluso si sigue a una elipsis que indica una expansión de paquete de parámetros, con lo que las siguientes plantillas de función son exactamente la misma: template<typename... Args>void f(Args..., ...);template<typename... Args>void f(Args... ...);template<typename... Args>void f(Args......); Un ejemplo de cuándo se podría usar esta declaración es la posible implementación de std::is_function. Ejecuta este código #include <cstdio> template<typename... Variadic, typename ...Args>constexprvoid invocar(auto(*fun)(Variadic......), Args... args){ fun(args...);} int main(){ invocar(std::printf, "%dm•%dm•%dm = %d%s%c", 2,3,7, 2*3*7, "m³", '\n');} Salida: 2m•3m•7m = 42m³ | (desde C++11) |
[editar]Definición de función
Una definición de función no miembro solamente puede aparecer en el ámbito de espacio de nombres (no hay funciones anidadas). Una definición de función miembro también puede aparecer en el cuerpo de una definición de clase. Tienen la siguiente sintaxis:
atributos(opcional)secuencia-de-especificadores-de-declaración(opcional)declaradorsecuencia-de-especificadores-virtuales(opcional)cuerpo-de-función | |||||||||
donde cuerpo-de-función es uno de los siguientes
inicializador-de-contructor(opcional)sentencia-compuesta | (1) | ||||||||
bloque-función-try | (2) | ||||||||
= delete ; | (3) | (desde C++11) | |||||||
= default ; | (4) | (desde C++11) | |||||||
atributos | - | (desde C++11) lista opcional de atributos. Estos atributos se combinan con los atributos posteriores al identificador en el declarador (vea el inicio de esta página), si hay. |
secuencia-de-especificadores-de-declaración | - | el tipo de retorno con especificadores, como en la gramática de declaración |
declarador | - | declarador de función, igual que en la gamática de declaración de función anterior. Al igual que con la declaración de función, puede ir seguida de una cláusula-requiere(desde C++20) |
secuencia-de-especificadores-virtuales | - | (desde C++11)override , final ,o su combinación en cualquier orden (solo se permite para funciones miembro no estáticas) |
inicializador-de-contructor | - | lista de inicializadores de miembro, se permite solo en constructores |
sentencia-compuesta | - | secuencia de sentencias encerradas entre llaves que constituye el cuerpo de la función |
int max(int a, int b, int c){int m =(a > b)? a : b;return(m > c)? m : c;} // la secuencia-de-despecificadores-de-declaración es "int"// el declarador es "max(int a, int b, int c)"// el cuerpo es { ... }
El cuerpo de la función es una sentencia compuesta (secuencia de cero o más sentencias encerradas por un par de llaves), que se ejecuta cuando se llama a la función.
Los tipos de los parámetros, así como el tipo de retorno de una función no pueden ser tipos claseincompletos a menos que la función se defina como eliminada(desde C++11). La comprobación de integridad se realiza en el contexto del cuerpo de la función, lo que permite que las funciones miembro devuelvan la clase en la que están definidas (o la clase que las contiene), incluso si en el punto de definición está incompleta (está completa en el cuerpo dela función).
Los parámetros declarados en el declarador de una definición de función están en el ámbito interno del cuerpo. Si no se usa un parámetro en el cuerpo de la función, no neceista ser nombrado (es suficiente con usar un declarador abstracto):
void escribir(int a, int)// no se usa el segundo parámtero{std::printf("a = %d\n", a);}
Aunque los calificadores cv de nivel superior en los parámetros se descartan en las declaraciones de función, modifican el tipo del parámetro como se ve en el cuerpo de un función:
void f(constint n)// declara una función de tipo void(int){// pero en el cuerpo, el tipo de n es const int}
Funciones eliminadasSi, en vez de un cuerpo de función, se usa la sintaxis especial Si la función está sobrecargada, primero tiene lugar la resolución de sobrecarga, y le programa está mal formado solamente si se seleccionó la función eliminada: struct alguntipo {void*operator new(std::size_t)= delete;void*operator new[](std::size_t)= delete;}; alguntipo* p = new alguntipo;// error: intenta llamar al eliminado alguntipo::operator new La definición de eliminación de una función debe ser la primera declaración en la unidad de traducción: una función declarada previamente no se puede redeclarar como eliminada: struct alguntipo { alguntipo();}; alguntipo ::alguntipo()= delete;// error: debe ser eliminada en la primera declaración __func__Dentro del cuerpo de la función, la variable predefinida local a la función __func__ se define como si fuera staticconstchar __func__[]="nombre-de-función"; Esta variable tiene ámbito de bloque y duración de almacenamiento estático: struct S { S(): s(__func__){}// correcto: la lista de incializadores es parte de cuerpo de la funciónconstchar* s;};void f(constchar* s = __func__);// error: la lista de parámetros es parte del declarador | (desde C++11) |
[editar]Notas
En caso de ambigüedad entre una declaración de variable usando la sintaxis de incialización directa y una declaración de función, el compilador siempre elige la declaración de función; vésae inicialización directa.
[editar]Ejemplo
#include <iostream>#include <string> // función simple con un argumento por defecto, sin valor devueltovoid f0(conststd::string& arg ="mundo!"){std::cout<<"¡Hola, "<< arg <<'\n';} // la declaración está en el ámbito de espacio de nombres (archivo)// (la definición se proporciona más tarde)int f1(); // función que devuelve un puntero a f0, estilo pre-C++11void(*fp03())(conststd::string&){return f0;} // función que devuelve un puntero a f0, con tipo de retorno final C++11 auto fp11()->void(*)(conststd::string&){return f0;} int main(){ f0(); fp03()("prueba!"); fp11()("otra vez!");int f2(std::string)noexcept;// declaración en ámbito de funciónstd::cout<<"f2(\"mal\"): "<< f2("mal")<<'\n';std::cout<<"f2(\"42\"): "<< f2("42")<<'\n';} // función no miembro simple que devuelve intint f1(){return007;} // función con una especificación de excepción y una bloque tryint f2(std::string str)noexcepttry{returnstd::stoi(str);}catch(conststd::exception& e){std::cerr<<"¡stoi() falla!\n";return0;}
Posible salida:
¡stoi() falla! ¡Hola, mundo! ¡Hola, prueba! ¡Hola, otra vez! f2("mal"): 0 f2("42"): 42
[editar]Informe de errores
Los siguientes informes de defectos de cambio de comportamiento se aplicaron de manera retroactiva a los estándares de C++ publicados anteriormente.
ID | Aplicado a | Comportamiento según lo publicado | Comportamiento correcto |
---|---|---|---|
CWG 135 | C++98 | funciones miembros definidas en una clase no pueden tener un parámetro de o devolver su propia clase porque es incompleta | se permite |
CWG 393 | C++98 | tipos que incluyen punteros/referencias a un array de límites desconocidos no pueden ser parámetros | se permiten dichos tipos |
CWG 452 | C++98 | la lista de inicializadores de miembro no es parte del cuerpo de la función | se hace parte del cuerpo de la función al modificar la sintaxis de la definición de función |
CWG 577 | C++98 | se podía usar el tipo void dependiente para declarar una función sin parámetros | solo se permite void no dependiente |
CWG 1394 | C++11 | la función eliminada no podía devolver un tipo incompleto | se permite el tipo de retorno incompleto |
[editar]Véase también
Documentación de C para Declaración de funciones |