¿El tipo de retorno es parte de la firma de la función?
En C++, ¿el tipo de retorno se considera parte de la firma de la función? y no se permite sobrecarga con solo modificar el tipo de retorno.
Las funciones normales no incluyen el tipo de devolución en su firma.
( nota : reescribí esta respuesta y los comentarios a continuación no se aplican a esta revisión; consulte el historial de ediciones para obtener más detalles).
Introducción
Sin embargo, la cuestión de las funciones y las declaraciones de funciones en el Estándar es complicada. Hay dos capas que deben considerarse:
- Declaraciones
- Entidades
La llamada declaración de función puede declarar una entidad de función o una entidad de plantilla. Si se declara una entidad de función, entonces tiene que hacerlo con una especialización explícita de una plantilla de función (con todos los argumentos especificados) o con una declaración de una función ordinaria. Si se declara una entidad de plantilla, entonces está declarando una plantilla de función principal o una especialización explícita donde no se especifican algunos argumentos. (Esto es muy similar a la relación entre "declaración de objeto" y objetos o referencias: la primera puede declarar un objeto o una referencia. Por lo tanto, una declaración de objeto no necesariamente declara un objeto).
El Estándar define la firma de una función para incluir lo siguiente en 1.3.10
:
Los tipos de sus parámetros y, si la función es un miembro de la clase, los calificadores cv- (si los hay) de la función misma y de la clase en la que se declara la función miembro. La firma de una especialización de plantilla de función incluye los tipos de argumentos de su plantilla. (14.5.5.1)
Falta el tipo de retorno en esta definición, que es parte de la firma de una especialización de plantilla de función (es decir, una declaración de función que declara una función que es una especialización de una plantilla), como lo señalan 14.5.5.1
(documentos de trabajo recientes de C++ 0x Se arregló eso para mencionar también el tipo de devolución 1.3.10
):
La firma de una especialización de plantilla de función consta de la firma de la plantilla de función y de los argumentos reales de la plantilla (ya sean especificados explícitamente o deducidos).
La firma de una plantilla de función consta de su firma de función, su tipo de retorno y su lista de parámetros de plantilla.
Entonces, ¿qué contiene exactamente una firma?
Entonces, cuando preguntamos sobre la firma de una función , tenemos que dar dos respuestas:
- Para funciones que son especializaciones de plantillas de funciones, la firma incluye el tipo de devolución.
- Para funciones que no son especializaciones, el tipo de retorno no forma parte de la firma.
Tenga en cuenta, sin embargo, que el tipo de retorno, en cualquier caso, es una parte importante del tipo de una función. Es decir, lo siguiente no es válido:
void f();
int (*pf)() = &f; // different types!
¿Cuándo una sobrecarga no es válida si solo difiere el tipo de devolución?
Los principales compiladores actualmente rechazan el siguiente código:
int f();
double f(); // invalid
Pero acepte el siguiente código:
template<typename T> int f();
template<typename T> double f(); // invalid?
Sin embargo, el Estándar prohíbe una declaración de función que solo difiera en el tipo de retorno (al definir cuándo una sobrecarga es válida y cuándo no). Sin embargo, no define con precisión qué significa "difiere sólo por el tipo de devolución".
Referencias de párrafos estándar:
- ¿Cuándo se puede sobrecargar una declaración de función?
13.1
- ¿Qué es una declaración de función?
7/2
y7/5
- ¿Cuál es la firma de una plantilla/especialización de función?
14.5.5.1
Como referencia, esto es lo que dice el borrador n3000 de C++ 0x más reciente sobre la "firma" en 1.3.11
, que es mucho más completo en su cobertura de los diferentes tipos de entidades:
el nombre y la lista de tipos de parámetros (8.3.5) de una función, así como la clase o espacio de nombres del que es miembro. Si una función o plantilla de función es un miembro de clase, su firma incluye adicionalmente los calificadores cv (si los hay) y el calificador ref (si los hay) en la función o plantilla de función en sí. La firma de una plantilla de función incluye además su tipo de retorno y su lista de parámetros de plantilla. La firma de una especialización de plantilla de función incluye la firma de la plantilla de la cual es una especialización y sus argumentos de plantilla (ya sean explícitamente especificados o deducidos). [Nota: Las firmas se utilizan como base para modificar y vincular nombres. - nota final]
Depende de si la función es una plantilla de función o no.
En Plantillas C++: las guías completas , Jusuttis proporciona una definición diferente a la dada en el estándar C++, pero con consecuencias equivalentes:
Definimos la firma de una función como la siguiente información:
- El nombre no calificado de la función.
- El ámbito de clase o espacio de nombres de ese nombre y, si el nombre tiene un vínculo interno, la unidad de traducción en la que se declara el nombre.
- La
const
,volatile
, oconst volatile
calificación de la función. - Los tipos de parámetros de función.
- su tipo de retorno , si la función se genera a partir de una plantilla de función
- Los parámetros de la plantilla y los argumentos de la plantilla , si la función se genera a partir de una plantilla de función.
Como sugirió litb , vale la pena aclarar por qué el tipo de devolución es parte de la firma de una función de plantilla.
Las funciones pueden coexistir en un programa si tienen firmas distintas.
. Dicho esto, si el tipo de retorno es un parámetro de plantilla:
template <typename T>
T foo(int a)
{return T();}
Es posible crear instancias de dos funciones que difieren sólo en el tipo de retorno:
foo<int>(0);
foo<char>(0);
No solo: como bien informa litb , también es posible sobrecargar dos funciones de plantilla, que difieren sólo en el tipo de retorno, incluso si el tipo de retorno no es un nombre dependiente. Aquí está su ejemplo:
template<class T> int foo(T)
{}
template<class T> bool foo(T)
{}
// at the instantiation point it is necessary to specify the cast
// in order not to face ambiguous overload
((int(*)(char))foo<char>)('a');
Son una parte suficiente del tipo que puede sobrecargar funciones basadas en tipos de puntero de función que difieren solo por el tipo de retorno:
int IntFunc() { return 0; }
char CharFunc() { return 0; }
void FuncFunc(int(*func)()) { cout << "int\n"; }
void FuncFunc(char(*func)()) { cout << "char\n"; }
int main()
{
FuncFunc(&IntFunc); // calls void FuncFunc(int_func func)
FuncFunc(&CharFunc); // calls void FuncFunc(char_func func)
}