Function declaration
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 la función.
Contenidos |
Declaración de función
Las declaraciones de funciones pueden aparecer en cualquier ámbito. Una declaración de función en el ámbito de clase introduce una función miembro de clase (a menos que se utilice el especificador friend ), consulte funciones miembro y funciones friend para más detalles.
noptr-declarator
(
parameter-list
)
cv
(opcional)
ref
(opcional)
except
(opcional)
attr
(opcional)
|
(1) | ||||||||
noptr-declarator
(
parameter-list
)
cv
(opcional)
ref
(opcional)
except
(opcional)
attr
(opcional)
->
trailing
|
(2) | (desde C++11) | |||||||
(consulte Declarations para las otras formas de la sintaxis del declarator )
| noptr-declarator | - |
cualquier
declarator
válido, pero si comienza con
*
,
&
, o
&&
, debe estar rodeado por paréntesis.
|
||||||
| parameter-list | - | lista posiblemente vacía, separada por comas de los parámetros de la función (ver abajo para detalles) | ||||||
| attr | - | (desde C++11) una lista de atributos . Estos atributos se aplican al tipo de la función, no a la función misma. Los atributos para 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 const/volatile, solo permitida en declaraciones de funciones miembro no estáticas | ||||||
| ref | - | (desde C++11) calificación de referencia, solo permitida en declaraciones de funciones miembro no estáticas | ||||||
| except | - |
|
||||||
| trailing | - | Tipo de retorno posterior, útil si el tipo de retorno depende de nombres de argumentos, como en template < class T, class U > auto add ( T t, U u ) - > decltype ( t + u ) ; o es complicado, como en auto fpif ( int ) - > int ( * ) ( int ) |
|
Como se menciona en Declarations , el declarador puede ir seguido de una requires clause , que declara las constraints asociadas para la función, las cuales deben satisfacerse para que la función sea seleccionada por overload resolution . (ejemplo: void f1 ( int a ) requires true ; ) Nótese que la constraint asociada es parte de la firma de la función, pero no forma parte del tipo de función. |
(since C++20) |
Los declaradores de función pueden mezclarse con otros declaradores, cuando la secuencia de especificadores de declaración lo permite:
// declara un int, un int*, una función, y un puntero a una función int a = 1, *p = NULL, f(), (*pf)(double); // decl-specifier-seq es int // el declarador f() declara (pero no define) // una función que no toma argumentos y retorna int struct S { virtual int f(char) const, g(int) &&; // declara dos funciones miembro no estáticas virtual int f(char), x; // error de compilación: virtual (en decl-specifier-seq) // solo está permitido en declaraciones de // funciones miembro no estáticas };
|
Usar un tipo de objeto calificado como volatile como tipo de parámetro o tipo de retorno está obsoleto. |
(since C++20) |
El tipo de retorno de una función no puede ser un tipo función o un tipo array (pero puede ser un puntero o referencia a estos).
|
Al igual que con cualquier declaración, los atributos que aparecen antes de la declaración y los atributos que aparecen inmediatamente después del identificador dentro del declarador se aplican ambos a la entidad que se declara o define (en este caso, a la función): [[noreturn]] void f [[noreturn]] (); // OK: both attributes apply to the function 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 misma: void f() [[noreturn]]; // Error: this attribute has no effect on the function itself |
(desde C++11) |
Deducción del tipo de retornoSi la secuencia-de-especificadores-de-declaración de la declaración de función contiene la palabra clave auto , el tipo de retorno posterior puede omitirse, y será deducido por el compilador del tipo del operando utilizado en la sentencia no-descartada return . Si el tipo de retorno no utiliza decltype ( auto ) , la deducción sigue las reglas de deducción de argumentos de plantilla : int x = 1; auto f() { return x; } // tipo de retorno es int const auto& f() { return x; } // tipo de retorno es const int&
Si el tipo de retorno es
decltype
(
auto
)
, el tipo de retorno es el que se obtendría si el operando utilizado en la sentencia return estuviera envuelto en
int x = 1; decltype(auto) f() { return x; } // tipo de retorno es int, igual que decltype(x) decltype(auto) f() { return(x); } // tipo de retorno es int&, igual que decltype((x)) (nota: “ const decltype ( auto ) & ” es un error, decltype ( auto ) debe usarse por sí solo) Si hay múltiples sentencias return, todas deben deducir al mismo tipo: auto f(bool val) { if (val) return 123; // deduce el tipo de retorno int else return 3.14f; // Error: deduce el tipo de retorno float } Si no hay una declaración de retorno o si el operando de la declaración de retorno es una expresión void (incluyendo declaraciones de retorno sin operando), el tipo de retorno declarado debe ser decltype ( auto ) , en cuyo caso el tipo de retorno deducido es void , o (posiblemente calificado cv) auto , en cuyo caso el tipo de retorno deducido es entonces (idénticamente calificado cv) void : auto f() {} // retorna void auto g() { return f(); } // retorna void auto* x() {} // Error: no se puede deducir auto* desde void Una vez que se ha visto una sentencia return en una función, el tipo de retorno deducido de esa sentencia puede usarse en el resto de la función, incluyendo en otras sentencias return: auto sum(int i) { if (i == 1) return i; // el tipo de retorno de sum es int else return sum(i - 1) + i; // OK: el tipo de retorno de sum ya es conocido } Si la sentencia return utiliza una lista de inicialización entre llaves , la deducción no está permitida: auto func() { return {1, 2, 3}; } // Error Funciones virtuales y coroutines (desde C++20) no pueden usar deducción de tipo de retorno: struct F { virtual auto f() { return 2; } // Error }; Plantillas de función distintas a las funciones de conversión definidas por el usuario pueden utilizar la deducción de tipo de retorno. La deducción ocurre en la instanciación incluso si la expresión en la sentencia return no es dependiente . Esta instanciación no está en un contexto inmediato para los propósitos de 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; } // instancia ambas f para determinar los tipos de retorno, // selecciona la segunda sobrecarga de plantilla Las redeclaraciones o especializaciones de funciones o plantillas de función que utilizan deducción del tipo de retorno deben usar los mismos marcadores de posición de tipo de retorno: auto f(int num) { return num; } // int f(int num); // Error: no tipo de retorno de marcador de posición // decltype(auto) f(int num); // Error: marcador de posición diferente template<typename T> auto g(T t) { return t; } template auto g(int); // OK: el tipo de retorno es int // template char g(char); // Error: no es una especialización de la plantilla principal g De manera similar, las redeclaraciones o especializaciones de funciones o plantillas de funciones que no utilizan deducción del tipo de retorno no deben usar un marcador de posición: int f(int num); // auto f(int num) { return num; } // Error: no es una redeclaración de f template<typename T> T g(T t) { return t; } template int g(int); // OK: especializa T como int // template auto g(char); // Error: no es una especialización de la plantilla principal g Declaraciones de instanciación explícita no instancian por sí mismas plantillas de función que utilizan deducción de tipo de retorno: template<typename T> auto f(T t) { return t; } extern template auto f(int); // no instancia f<int> int (*p)(int) = f; // instancia f<int> para determinar su tipo de retorno, // pero aún se requiere una definición de instanciación explícita // en algún lugar del programa |
(desde C++14) |
Lista de parámetros
La lista de parámetros determina los argumentos que pueden especificarse cuando se llama a la función. Es una lista separada por comas de declaraciones de parámetros , cada una de las cuales tiene la siguiente sintaxis:
| attr (opcional) decl-specifier-seq declarator | (1) | ||||||||
|
attr
(opcional)
|
(2) | (desde C++23) | |||||||
attr
(opcional)
decl-specifier-seq
declarator
=
initializer
|
(3) | ||||||||
| attr (opcional) decl-specifier-seq abstract-declarator (opcional) | (4) | ||||||||
|
attr
(opcional)
|
(5) | (desde C++23) | |||||||
attr
(opcional)
decl-specifier-seq
abstract-declarator
(opcional)
=
initializer
|
(6) | ||||||||
void
|
(7) | ||||||||
| Uso incorrecto | Ejemplo |
|---|---|
| hay múltiples parámetros presentes | int f1 ( void , int ) ; |
| el parámetro void está nombrado | inf f2 ( void param ) ; |
| void está calificado cv | int f3 ( const void ) ; |
| void es dependiente |
int
f4
(
T
)
;
(donde
T
es
void
)
|
| el parámetro void es un parámetro de objeto explícito (desde C++23) | int f5 ( this void ) ; |
|
Aunque decl-specifier-seq implica que pueden existir especificadores distintos a los especificadores de tipo, el único otro especificador permitido es register así como auto (hasta C++11) , y no tiene efecto. |
(hasta C++17) |
|
Si alguno de los parámetros de la función utiliza un marcador de posición (ya sea auto o un tipo concepto ), la declaración de función es en cambio una declaración de plantilla de función abreviada : void f1(auto); // same as template<class T> void f1(T) void f2(C1 auto); // same as template<C1 T> void f2(T), if C1 is a concept |
(desde C++20) |
|
Una declaración de parámetro con el especificador this (sintaxis ( 2 ) / ( 5 ) ) declara un parámetro de objeto explícito . Un parámetro de objeto explícito no puede ser un paquete de parámetros de función , y solo puede aparecer como el primer parámetro de la lista de parámetros en las siguientes declaraciones:
Una función miembro con un parámetro de objeto explícito tiene las siguientes restricciones:
struct C { void f(this C& self); // OK template<typename Self> void g(this Self&& self); // also OK for templates void p(this C) const; // Error: “const” not allowed here static void q(this C); // Error: “static” not allowed here void r(int, this C); // Error: an explicit object parameter // can only be the first parameter }; // void func(this C& self); // Error: non-member functions cannot have // an explicit object parameter |
(desde C++23) |
Los nombres de parámetros declarados en declaraciones de funciones son generalmente solo con fines de auto-documentación. Se utilizan (pero siguen siendo opcionales) en definiciones de funciones.
Surge una ambigüedad en una lista de parámetros cuando un nombre de tipo está anidado entre paréntesis (incluyendo lambda expressions ) (since C++11) . En este caso, la elección está entre la declaración de un parámetro de tipo puntero a función y la declaración de un parámetro con paréntesis redundantes alrededor del identificador del declarator . La resolución es considerar el nombre de tipo como un simple type specifier (que es el tipo puntero a función):
class C {}; void f(int(C)) {} // void f(int(*fp)(C param)) {} // NO void f(int C) {} void g(int *(C[10])); // void g(int *(*fp)(C param[10])); // NO void g(int *C[10]);
El tipo de parámetro no puede ser un tipo que incluya una referencia o un puntero a un array de límite desconocido, incluyendo punteros/arrays multinivel de tales tipos, o un puntero a funciones cuyos parámetros sean tales tipos.
Uso de puntos suspensivos
El último parámetro en la lista de parámetros puede ser una elipsis ( ... ); esto declara una función variádica . La coma que precede a la elipsis puede omitirse (obsoleto en C++26) :
int printf(const char* fmt, ...); // una función variádica int printf(const char* fmt...); // igual que la anterior, pero obsoleta desde C++26 template<typename... Args> void f(Args..., ...); // una plantilla de función variádica con un paquete de parámetros template<typename... Args> void f(Args... ...); // igual que la anterior, pero obsoleta desde C++26 template<typename... Args> void f(Args......); // igual que la anterior, pero obsoleta desde C++26
Tipo de función
Lista de tipos de parámetros
La parameter-type-list de una función se determina de la siguiente manera:
- El tipo de cada parámetro (incluyendo los parameter packs de función) (since C++11) se determina a partir de su propia declaración de parámetro .
-
Después de determinar el tipo de cada parámetro, cualquier parámetro de tipo "array de
T" o de tipo funciónTse ajusta a "puntero aT". - Después de producir la lista de tipos de parámetros, cualquier cv-qualifier de nivel superior que modifique un tipo de parámetro se elimina al formar el tipo de función.
- La lista resultante de tipos de parámetros transformados y la presencia o ausencia de la elipsis o un parameter pack de función (since C++11) constituye la lista de tipos de parámetros de la función.
void f(char*); // #1 void f(char[]) {} // define #1 void f(const char*) {} // OK, otra sobrecarga void f(char* const) {} // Error: redefine #1 void g(char(*)[2]); // #2 void g(char[3][2]) {} // define #2 void g(char[3][3]) {} // OK, otra sobrecarga void h(int x(const int)); // #3 void h(int (*)(int)) {} // define #3
Determinación del tipo de función
En la sintaxis
(1)
, asumiendo que
noptr-declarator
es una declaración independiente, dado el tipo del
qualified-id
o
unqualified-id
en
noptr-declarator
como "derived-declarator-type-list
T
":
|
(desde C++17) |
-
El
(hasta C++17)
En caso contrario, el
(desde C++17)
tipo de la función declarada es
"lista-tipo-declarador-derivada función de
lista-tipo-parámetro cv (opcional) ref (opcional) (desde C++11) que retornaT".
|
En la sintaxis
(2)
, asumiendo
noptr-declarator
como una declaración independiente, dado el tipo del
qualified-id
o
unqualified-id
en
noptr-declarator
como "derived-declarator-type-list
|
(desde C++11) |
|
(desde C++17) |
attr , si está presente, se aplica al tipo de función. |
(desde C++11) |
// el tipo de "f1" es // "función de int que retorna void, con atributo noreturn" void f1(int a) [[noreturn]]; // el tipo de "f2" es // "función constexpr noexcept de puntero a int que retorna int" constexpr auto f2(int[] b) noexcept -> int; struct X { // el tipo de "f3" es // "función de ningún parámetro const que retorna const int" const int f3() const; };
Calificadores finales
Un tipo de función con
cv
o
ref
(desde C++11)
(incluyendo un tipo nombrado por
typedef
name) puede aparecer solamente como:
- el tipo de función para una función miembro no estática ,
- el tipo de función al que se refiere un puntero a miembro,
- el tipo de función de nivel superior de una declaración typedef o declaración de alias (desde C++11) ,
- el type-id en el argumento por defecto de un parámetro de plantilla de tipo , o
- el type-id de un argumento de plantilla para un parámetro de plantilla de tipo.
typedef int FIC(int) const; FIC f; // Error: no declara una función miembro struct S { FIC f; // OK }; FIC S::*pm = &S::f; // OK
Firma de la función
Cada función tiene una firma.
La firma de una función consiste en su nombre y parameter-type-list . Su firma también incluye el namespace que la contiene, con las siguientes excepciones:
- Si la función es una member function , su firma contiene la clase de la cual la función es miembro en lugar del namespace contenedor. Su firma también contiene los siguientes componentes, si existen:
-
- cv
|
(desde C++11) |
|
(desde C++20) |
excepto y attr (since C++11) no involucra la firma de la función , aunque noexcept specification afecta el tipo de función (since C++17) .
Definición de función
Una definición de función no miembro puede aparecer únicamente en el ámbito del espacio de nombres (no existen funciones anidadas). Una función miembro definición también puede aparecer en el cuerpo de una definición de clase . Tienen la siguiente sintaxis:
|
attr
(opcional)
decl-specifier-seq
(opcional)
declarator
virt-specs (opcional) contract-specs (opcional) function-body |
(1) | ||||||||
|
attr
(opcional)
decl-specifier-seq
(opcional)
declarator
requires-clause contract-specs (opcional) function-body |
(2) | (desde C++20) | |||||||
| attr | - | (desde C++11) una lista de atributos . Estos atributos se combinan con los atributos después del identificador en el declarador (ver arriba de esta página), si los hay. |
| decl-specifier-seq | - | el tipo de retorno con especificadores, como en la gramática de declaraciones |
| declarator | - | declarador de función, igual que en la gramática de declaración de función anterior (puede estar entre paréntesis) |
| virt-specs | - |
(desde C++11)
override
,
final
, o su combinación en cualquier orden
|
| requires-clause | - | una requires cláusula |
| contract-specs | - | (desde C++26) una lista de especificadores de contrato de función |
| function-body | - | el cuerpo de la función (ver abajo) |
function-body
es una de las siguientes:
| ctor-initializer (opcional) compound-statement | (1) | ||||||||
| function-try-block | (2) | ||||||||
=
default
;
|
(3) | (desde C++11) | |||||||
=
delete
;
|
(4) | (desde C++11) | |||||||
=
delete
(
string-literal
);
|
(5) | (desde C++26) | |||||||
| ctor-initializer | - | member initializer list , solo permitido en constructores |
| compound-statement | - | la secuencia de instrucciones entre llaves que constituye el cuerpo de una función |
| function-try-block | - | un bloque try de función |
| string-literal | - | un literal de cadena no evaluado que podría utilizarse para explicar la razón por la cual la función está eliminada |
int max(int a, int b, int c) { int m = (a > b) ? a : b; return (m > c) ? m : c; } // la secuencia de especificadores 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 compound statement (secuencia de cero o más statements rodeados por un par de llaves), que se ejecuta cuando se realiza la llamada a la función. Además, el cuerpo de la función de un constructor también incluye lo siguiente:
- Para todos los miembros de datos no estáticos cuyos identificadores están ausentes en la lista de inicialización de miembros del constructor, las inicializaciones predeterminadas de miembros o (since C++11) inicializaciones predeterminadas se utilizan para inicializar los correspondientes subobjetos miembros.
- Para todas las clases base cuyos nombres de tipo están ausentes en la lista de inicialización de miembros del constructor, las inicializaciones predeterminadas se utilizan para inicializar los correspondientes subobjetos de clase base.
|
Si una definición de función contiene un virt-specs , debe definir una función miembro . |
(desde C++11) |
|
Si una definición de función contiene una requires-clause , debe definir una función plantilla . |
(desde C++20) |
void f() override {} // Error: no es una función miembro void g() requires (sizeof(int) == 4) {} // Error: no es una función plantilla
Los tipos de parámetros, así como el tipo de retorno de una definición de función no pueden ser (posiblemente calificados cv) tipos de clase incompletos class types a menos que la función esté definida como eliminada (since C++11) . La verificación de completitud solo se realiza en el cuerpo de la función, lo que permite que las member functions retornen la clase en la que están definidas (o su clase envolvente), incluso si está incompleta en el punto de definición (está completa en el cuerpo de la función).
Los parámetros declarados en el declarador de una definición de función están en ámbito dentro del cuerpo. Si un parámetro no se utiliza en el cuerpo de la función, no necesita ser nombrado (es suficiente usar un declarador abstracto):
void print(int a, int) // segundo parámetro no se utiliza { std::printf("a = %d\n", a); }
Aunque los cv-qualifiers de nivel superior en los parámetros se descartan en las declaraciones de funciones, modifican el tipo del parámetro tal como es visible en el cuerpo de una función:
void f(const int n) // declara una función de tipo void(int) { // pero en el cuerpo, el tipo de "n" es const int }
Funciones por defectoSi la definición de la función es de sintaxis ( 3 ) , la función se define como explícitamente predeterminada . Una función que está explícitamente predeterminada debe ser una función miembro especial o función de operador de comparación (desde C++20) , y no debe tener argumentos predeterminados .
Una función miembro especial explícitamente definida por defecto
Si el tipo de
Una función explícitamente definida por defecto en su primera declaración es implícitamente inline , y es implícitamente constexpr si puede ser una constexpr function . struct S { S(int a = 0) = default; // error: argumento por defecto void operator=(const S&) = default; // error: tipo de retorno no coincide ~S() noexcept(false) = default; // OK, especificación de excepción diferente private: int i; S(S&); // OK, constructor de copia privado }; S::S(S&) = default; // OK, define el constructor de copia Las funciones explícitamente predeterminadas y las funciones implícitamente declaradas se denominan colectivamente funciones predeterminadas . Sus definiciones reales serán proporcionadas implícitamente, consulte sus páginas correspondientes para más detalles. Funciones eliminadasSi la definición de la función es de sintaxis ( 4 ) o ( 5 ) (since C++26) , la función se define como explícitamente eliminada . Cualquier uso de una función eliminada está mal formado (el programa no compilará). Esto incluye llamadas, tanto explícitas (con un operador de llamada a función) como implícitas (una llamada a un operador sobrecargado eliminado, función miembro especial, función de asignación, etc.), construir un puntero o puntero-a-miembro hacia una función eliminada, e incluso el uso de una función eliminada en una expresión que no es potencialmente evaluada . Una función miembro virtual no pura puede definirse como eliminada, aunque esté implícitamente odr-used . Una función eliminada solo puede ser reemplazada por funciones eliminadas, y una función no eliminada solo puede ser reemplazada por funciones no eliminadas.
Si la función está sobrecargada, overload resolution ocurre primero, y el programa solo está mal formado si la función eliminada fue seleccionada: struct T { void* operator new(std::size_t) = delete; void* operator new[](std::size_t) = delete("new[] is deleted"); // desde C++26 }; T* p = new T; // Error: intenta llamar a T::operator new eliminado T* p = new T[5]; // Error: intenta llamar a T::operator new[] eliminado, // emite un mensaje de diagnóstico “new[] is deleted” La definición eliminada de una función debe ser la primera declaración en una unidad de traducción: una función previamente declarada no puede ser redeclarada como eliminada: struct T { T(); }; T::T() = delete; // Error: debe ser eliminado en la primera declaración Funciones proporcionadas por el usuarioUna función es proporcionada por el usuario si es declarada por el usuario y no está explícitamente definida por defecto o eliminada en su primera declaración. Una función explícitamente definida por defecto proporcionada por el usuario (es decir, explícitamente definida por defecto después de su primera declaración) se define en el punto donde se define explícitamente por defecto; si dicha función se define implícitamente como eliminada, el programa está mal formado. Declarar una función como definida por defecto después de su primera declaración puede proporcionar ejecución eficiente y definición concisa mientras permite una interfaz binaria estable para una base de código en evolución. // Todas las funciones miembro especiales de “trivial” son // definidas por defecto en sus primeras declaraciones respectivamente, // no son proporcionadas por el usuario struct trivial { trivial() = default; trivial(const trivial&) = default; trivial(trivial&&) = default; trivial& operator=(const trivial&) = default; trivial& operator=(trivial&&) = default; ~trivial() = default; }; struct nontrivial { nontrivial(); // primera declaración }; // no definida por defecto en la primera declaración, // es proporcionada por el usuario y está definida aquí nontrivial::nontrivial() = default; Resolución de Ambigüedad
En caso de ambigüedad entre un cuerpo de función y un
inicializador
que comienza con
using T = void(); // tipo de función using U = int; // tipo no-función T a{}; // define una función que no hace nada U b{}; // inicializa por valor un objeto int T c = delete("hello"); // define una función como eliminada U d = delete("hello"); // inicializa por copia un objeto int con // el resultado de una expresión delete (mal formado)
__func__Dentro del cuerpo de la función, la variable predefinida local de la función __func__ se define como si fuera por static const char __func__[] = "nombre-de-función"; Esta variable tiene ámbito de bloque y duración de almacenamiento estático: struct S { S(): s(__func__) {} // OK: la lista de inicializadores es parte del cuerpo de la función const char* s; }; void f(const char* s = __func__); // Error: la lista de parámetros es parte del declarador
Ejecutar este código
Salida posible: Foo Bar Pub ~Bar |
(desde C++11) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Especificadores de contrato de funciónLas declaraciones de funciones y las expresiones lambda pueden contener una secuencia de especificadores de contrato de función , cada especificador tiene la siguiente sintaxis:
1)
Introduce una
aserción de precondición
.
2,3)
Introduce una
aserción de postcondición
.
2)
La aserción no se vincula al resultado.
3)
La aserción se vincula al resultado.
Una aserción de contrato de función es una aserción de contrato asociada con una función. El predicado de una aserción de contrato de función es su predicado convertido contextualmente a bool . Las siguientes funciones no pueden declararse con especificadores de contrato de función:
Aserciones de precondiciónUna aserción de precondición está asociada con la entrada a una función: int divide(int dividend, int divisor) pre(divisor != 0) { return dividend / divisor; } double square_root(double num) pre(num >= 0) { return std::sqrt(num); } Aserciones de postcondiciónUna aserción de postcondición está asociada con la salida normal de una función. Si una aserción de postcondición tiene un identifier , el especificador de contrato de función introduce identifier como el nombre de un result binding de la función asociada. Un result binding denota el objeto o referencia devuelto por la invocación de esa función. El tipo de un result binding es el tipo de retorno de su función asociada. int absolute_value(int num) post(r : r >= 0) { return std::abs(num); } double sine(double num) post(r : r >= -1.0 && r <= 1.0) { if (std::isnan(num) || std::isinf(num)) // salir mediante una excepción nunca causa violación de contrato throw std::invalid_argument("Argumento inválido"); return std::sin(num); } Si una aserción de postcondición tiene un identificador , y el tipo de retorno de la función asociada es (posiblemente calificado-cv) void , el programa está mal formado: void f() post(r : r > 0); // Error: ningún valor puede ser asignado a "r" Cuando el tipo de retorno declarado de una función no parametrizada contiene un tipo de marcador de posición , una aserción de poscondición con un identificador solo puede aparecer en una definición de función: auto g(auto&) post(r : r >= 0); // OK, "g" es una plantilla auto h() post(r : r >= 0); // Error: no se puede nombrar el valor de retorno auto k() post(r : r >= 0) // OK, "k" es una definición { return 0; } Consistencia de contrato
Una
redeclaración
Si una declaración
Dos contract-specs son iguales si consisten en los mismos especificadores de contrato de función en el mismo orden.
Un especificador de contrato de función
Si esta condición no se cumple únicamente debido a la comparación de dos expresiones lambda que están contenidas dentro de los predicate s, no se requiere ningún diagnóstico. bool b1, b2; void f() pre (b1) pre([]{ return b2; }()); void f(); // OK, especificadores de contrato de función omitidos void f() pre (b1) pre([]{ return b2; }()); // Error: los closures tienen tipos diferentes void f() pre (b1); // Error: los especificadores de contrato de función son diferentes int g() post(r : b1); int g() post(b1); // Error: no hay vinculación de resultado namespace N { void h() pre (b1); bool b1; void h() pre (b1); // Error: los especificadores de contrato de función difieren // según la regla de una definición } |
(desde C++26) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Notas
En caso de ambigüedad entre una declaración de variable usando la sintaxis de inicialización directa y una declaración de función, el compilador siempre elige la declaración de función; consulte direct-initialization .
| Macro de prueba de características | Valor | Estándar | Característica |
|---|---|---|---|
__cpp_decltype_auto
|
201304L
|
(C++14) |
decltype(auto)
|
__cpp_return_type_deduction
|
201304L
|
(C++14) | deducción del tipo de retorno para funciones normales |
__cpp_explicit_this_parameter
|
202110L
|
(C++23) | parámetros de objeto explícitos ( deducción de this ) |
__cpp_deleted_function
|
202403L
|
(C++26) | función eliminada con razón |
Palabras clave
Ejemplo
#include <iostream> #include <string> // función simple con un argumento predeterminado, que no retorna nada void f0(const std::string& arg = "world!") { std::cout << "Hello, " << arg << '\n'; } // la declaración está en el ámbito del namespace (archivo) // (la definición se proporciona más adelante) int f1(); // función que retorna un puntero a f0, estilo pre-C++11 void (*fp03())(const std::string&) { return f0; } // función que retorna un puntero a f0, con tipo de retorno posterior de C++11 auto fp11() -> void(*)(const std::string&) { return f0; } int main() { f0(); fp03()("test!"); fp11()("again!"); int f2(std::string) noexcept; // declaración en ámbito de función std::cout << "f2(\"bad\"): " << f2("bad") << '\n'; std::cout << "f2(\"42\"): " << f2("42") << '\n'; } // función simple no miembro que retorna int int f1() { return 007; } // función con especificación de excepción y bloque try de función int f2(std::string str) noexcept try { return std::stoi(str); } catch (const std::exception& e) { std::cerr << "stoi() failed!\n"; return 0; } // función eliminada, un intento de llamarla resulta en un error de compilación void bar() = delete # if __cpp_deleted_function ("reason") # endif ;
Salida posible:
stoi() failed!
Hello, world!
Hello, test!
Hello, again!
f2("bad"): 0
f2("42"): 42
Informes de defectos
Los siguientes informes de defectos que modifican el comportamiento se aplicaron retroactivamente a los estándares publicados anteriormente de C++.
| DR | Aplicado a | Comportamiento publicado | Comportamiento correcto |
|---|---|---|---|
| CWG 135 | C++98 |
las funciones miembro definidas en clase
no podían tener un parámetro de o retornar su propia clase porque está incompleta |
permitido |
| CWG 332 | C++98 | un parámetro podría tener tipo void calificado cv | prohibido |
| CWG 393 | C++98 |
tipos que incluyen punteros/referencias a
arreglo de límite desconocido no podían ser parámetros |
tales tipos están permitidos |
| CWG 452 | C++98 | la lista de inicialización de miembros no era parte del cuerpo de función | lo es |
| CWG 577 | C++98 |
el tipo dependiente
void
podría usarse para
declarar una función sin parámetros |
solo se permite
void no dependiente |
| CWG 1327 | C++11 |
funciones predeterminadas o eliminadas no podían
especificarse con override o final |
permitido |
| CWG 1355 | C++11 | solo las funciones miembro especiales podían ser proporcionadas por el usuario | extendido a todas las funciones |
| CWG 1394 | C++11 |
funciones eliminadas no podían tener ningún parámetro de
un tipo incompleto o retornar un tipo incompleto |
tipo incompleto permitido |
| CWG 1824 | C++98 |
la verificación de completitud en el tipo de parámetro y
tipo de retorno de una definición de función podría hacerse fuera del contexto de la definición de función |
solo verificar en el
contexto de la definición de función |
| CWG 1877 | C++14 | la deducción de tipo de retorno trataba return ; como return void ( ) ; |
simplemente deducir el tipo
de retorno como void en este caso |
| CWG 2015 | C++11 |
el uso odr implícito de una función
virtual eliminada era incorrecto |
tales usos odr están exentos
de la prohibición de uso |
| CWG 2044 | C++14 |
la deducción de tipo de retorno en funciones que retornan
void
fallaría si el tipo de retorno declarado es decltype ( auto ) |
actualizada la regla de
deducción para manejar este caso |
| CWG 2081 | C++14 |
las redeclaraciones de función podían usar deducción
de tipo de retorno incluso si la declaración inicial no lo hace |
no permitido |
| CWG 2144 | C++11 | { } podría ser un cuerpo de función o un inicializador en el mismo lugar |
diferenciado por el tipo
del identificador del declarador |
| CWG 2145 | C++98 | el declarador en la definición de función no podía estar entre paréntesis | permitido |
| CWG 2259 | C++11 |
la regla de resolución de ambigüedad respecto a nombres
de tipo entre paréntesis no cubría expresiones lambda |
cubierto |
| CWG 2430 | C++98 |
en la definición de una función miembro en una definición de clase,
el tipo de esa clase no podía ser el tipo de retorno o tipo de parámetro debido a la resolución de CWG issue 1824 |
solo verificar en el
cuerpo de función |
| CWG 2760 | C++98 |
el cuerpo de función de un constructor no incluía las inicializaciones
no especificadas en el cuerpo de función regular del constructor |
también incluye estas
inicializaciones |
| CWG 2831 | C++20 |
una definición de función con una
cláusula-requires
podría definir una función no template |
prohibido |
| CWG 2846 | C++23 | las funciones miembro de objeto explícito no podían tener definiciones fuera de clase | permitido |
| CWG 2915 | C++23 | los parámetros de objeto explícito sin nombre podían tener tipo void | prohibido |
Véase también
|
Documentación de C
para
Declaración de funciones
|