Namespaces
Variants

Function declaration

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

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 )

1) Sintaxis de declaración de función regular.
2) Declaración de tipo de retorno final. El decl-specifier-seq en este caso debe contener la palabra clave auto .
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 -

especificación dinámica de excepciones

(hasta C++11)

ya sea especificación dinámica de excepciones
o especificación noexcept

(desde C++11)
(hasta C++17)

especificación noexcept

(desde C++17)
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 retorno

Si 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 decltype :

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) this decl-specifier-seq declarator

(2) (desde C++23)
attr  (opcional) decl-specifier-seq declarator = initializer (3)
attr  (opcional) decl-specifier-seq abstract-declarator  (opcional) (4)

attr  (opcional) this decl-specifier-seq abstract-declarator  (opcional)

(5) (desde C++23)
attr  (opcional) decl-specifier-seq abstract-declarator  (opcional) = initializer (6)
void (7)
1) Declara un parámetro nombrado (formal). Para los significados de decl-specifier-seq y declarator , consulte declarations .
int f ( int a, int * p, int ( * ( * x ) ( double ) ) [ 3 ] ) ;
2) Declara un parámetro de objeto explícito con nombre.
3) Declara un parámetro nombrado (formal) con un valor predeterminado .
int f ( int a = 7 , int * p = nullptr, int ( * ( * x ) ( double ) ) [ 3 ] = nullptr ) ;
4) Declara un parámetro sin nombre.
int f ( int , int * , int ( * ( * ) ( double ) ) [ 3 ] ) ;
5) Declara un explicit object parameter sin nombre.
6) Declara un parámetro sin nombre con un valor predeterminado .
int f ( int = 7 , int * = nullptr, int ( * ( * ) ( double ) ) [ 3 ] = nullptr ) ;
7) Indica que la función no toma parámetros, es el sinónimo exacto de una lista de parámetros vacía: int f ( void ) ; y int f ( ) ; declaran la misma función.
void es la única sintaxis equivalente a una lista de parámetros vacía, otros usos de void parámetros son incorrectos:
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:

  • La función no es static .
  • La función no es virtual .
  • El declarador de la función no contiene cv ni ref .
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:

  1. 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 .
  2. Después de determinar el tipo de cada parámetro, cualquier parámetro de tipo "array de T " o de tipo función T se ajusta a "puntero a T ".
  3. 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.
  4. 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 ":

  • Si la especificación de excepción es no-lanzadora , el tipo de la función declarada es
    “lista-de-tipos-de-declarador-derivado noexcept función de
    lista-de-tipos-de-parámetro cv  (opcional) ref   (opcional) retornando 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 retorna T ".

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 T " ( T debe ser auto en este caso):

(desde C++11)
  • Si la especificación de excepción es no-lanzadora , el tipo de la función declarada es
    "derived-declarator-type-list noexcept función de
    parameter-type-list cv  (opcional) ref   (opcional) retornando trailing ".
(desde C++17)
  • El (hasta C++17) De lo contrario, el (desde C++17) tipo de la función declarada es
    "derived-declarator-type-list función de
    parameter-type-list cv  (opcional) ref   (opcional) retornando trailing ".

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:

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
  • ref
(desde C++11)
  • cláusula requires final
  • Si la función es una función friend no plantilla con una cláusula requires final, su firma contiene la clase envolvente en lugar del espacio de nombres envolvente. La firma también contiene la cláusula requires final.
(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)
1) Una definición de función sin restricciones.
2) Una definición de función con restricciones.
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)
1) Cuerpo de función regular.
3) Definición de función explícitamente predeterminada.
4) Definición de función explícitamente eliminada.
5) Definición de función explícitamente eliminada con mensaje de error.
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:

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 defecto

Si 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 F1 puede diferir de la función miembro especial correspondiente F2 que habría sido declarada implícitamente, de la siguiente manera:

  • F1 y F2 pueden tener diferentes ref y/o except .
  • Si F2 tiene un parámetro no-objeto de tipo const C & , el parámetro no-objeto correspondiente de F1 puede ser de tipo C& .
  • Si F2 tiene un parámetro de objeto implícito de tipo "referencia a C ", F1 puede ser una función miembro de objeto explícito cuyo parámetro de objeto explícito sea de tipo (posiblemente diferente) "referencia a C ", en cuyo caso el tipo de F1 diferiría del tipo de F2 en que el tipo de F1 tiene un parámetro adicional.
(desde C++23)

Si el tipo de F1 difiere del tipo de F2 de una manera distinta a la permitida por las reglas anteriores, entonces:

  • Si F1 es un operador de asignación, y el tipo de retorno de F1 difiere del tipo de retorno de F2 o el tipo de parámetro no-objeto de F1 no es una referencia, el programa está mal formado.
  • De lo contrario, si F1 está explícitamente definido por defecto en su primera declaración, se define como eliminado.
  • De lo contrario, el programa está mal formado.

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 eliminadas

Si 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 string-literal está presente, se recomienda que la implementación incluya su texto como parte del mensaje de diagnóstico resultante que muestra la justificación para la eliminación o para sugerir una alternativa.

(since C++26)

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 usuario

Una 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 { o = (desde C++26) , la ambigüedad se resuelve comprobando el tipo del identificador declarador de noptr-declarator :

  • Si el tipo es un tipo de función, la secuencia de tokens ambigua se trata como un cuerpo de función.
  • De lo contrario, la secuencia de tokens ambigua se trata como un inicializador.
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
#include <iostream>
void Foo() { std::cout << __func__ << ' '; }
struct Bar
{
    Bar() { std::cout << __func__ << ' '; }
    ~Bar() { std::cout << __func__ << ' '; }
    struct Pub { Pub() { std::cout << __func__ << ' '; } };
};
int main()
{
    Foo();
    Bar bar;
    Bar::Pub pub;
}

Salida posible:

Foo Bar Pub ~Bar
(desde C++11)

Especificadores de contrato de función

Las declaraciones de funciones y las expresiones lambda pueden contener una secuencia de especificadores de contrato de función  , cada especificador tiene la siguiente sintaxis:

pre attr  (opcional) ( predicate ) (1)
post attr  (opcional) ( predicate ) (2)
post attr  (opcional) ( identifier result-attr  (opcional) : predicate ) (3)
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.
attr - una lista de atributos pertenecientes a la aserción de contrato introducida
predicate - cualquier expresión (excepto expresiones de coma sin paréntesis)
identifier - el identificador que se refiere al resultado
result-attr - una lista de atributos pertenecientes al enlace del resultado


La aserción de precondición y la aserción de postcondición se denominan colectivamente aserción de contrato de función  .

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ón

Una 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ón

Una 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 D de una función o plantilla de función func debe tener ya sea ninguna contract-specs o las mismas contract-specs que cualquier primera declaración F accesible desde D . Si D y F están en diferentes unidades de traducción, se requiere un diagnóstico solo si D está adjunta a un módulo con nombre.

Si una declaración F1 es la primera declaración de func en una unidad de traducción y una declaración F2 es la primera declaración de func en otra unidad de traducción, F1 y F2 deben especificar los mismos contract-specs , no se requiere diagnóstico.

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 C1 en una declaración de función D1 es el mismo que un especificador de contrato de función C2 en una declaración de función D2 si se satisfacen todas las siguientes condiciones:

  • Los predicados de C1 y C2 satisfarían la regla de una definición si se colocaran en definiciones de función en las declaraciones D1 y D2 (si D1 y D2 están en diferentes unidades de traducción, las entidades correspondientes definidas dentro de cada predicado se comportan como si hubiera una única entidad con una sola definición), respectivamente, excepto por los siguientes renombramientos:
    • El renombramiento de los parámetros de la función declarada.
    • El renombramiento de los parámetros de plantilla de una plantilla que envuelve la función declarada.
    • El renombramiento del enlace de resultado (si existe).
  • Tanto C1 como C2 tienen un identificador o ninguno lo tiene.

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

default , delete , pre , post

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