Menu

Sobrecarga de funciones en C++: mismo nombre, distintos parámetros

La sobrecarga de funciones en C++ permite que varias funciones compartan un mismo nombre siempre que sus listas de parámetros difieran. Aprende cómo la resolución de sobrecarga elige la versión correcta, por qué el tipo de retorno por sí solo no cuenta y las trampas de ambigüedad y argumentos por defecto que debes evitar.

Esta página incluye editores ejecutables: edita, ejecuta y ve el resultado al instante.

Un nombre, muchas versiones

A menudo necesitas la misma operación para distintos tipos de datos: imprimir un int, imprimir un string, imprimir un double. En algunos lenguajes inventarías printInt, printString, printDouble. C++ te permite darles a todas el mismo nombre y las distingue por sus parámetros. Eso es la sobrecarga de funciones.

La regla es sencilla: varias funciones pueden compartir un nombre siempre que sus listas de parámetros difieran, ya sea en el número de parámetros, sus tipos o su orden. El compilador examina los argumentos en cada punto de llamada y elige por ti la versión que coincide.

Tres funciones, un nombre. Cada llamada cae en la versión cuyo tipo de parámetro encaja con el argumento. Esto es lo que hace que std::cout << x funcione igual para ints, doubles y strings: operator<< está sobrecargado muchísimas veces.

Qué cuenta como una sobrecarga distinta

Una sobrecarga se distingue únicamente por su lista de parámetros. Puedes variar:

int  area(int side);                 // 1 parámetro
int  area(int width, int height);    // 2 parámetros  -> distinta
double area(double r);               // tipo distinto -> distinta

void log(string msg, int level);     // el orden importa...
void log(int level, string msg);     // ...así que esta también es distinta

Cada una de ellas es una sobrecarga legal y diferenciada. El compilador construye un conjunto de candidatas a partir de todas las funciones llamadas area y luego hace la coincidencia por número y tipo de argumentos.

El tipo de retorno por sí solo no basta

Aquí está la trampa que hace tropezar a casi todo el mundo: no puedes sobrecargar por el tipo de retorno. El valor devuelto no interviene en la elección de la sobrecarga, porque el compilador decide qué función llamar a partir de los argumentos, antes de mirar siquiera qué devuelve.

int    convert(double x);   // OK
double convert(double x);   // ERROR: redefinición - solo difiere el tipo de retorno

Eso no compila. Si las listas de parámetros son idénticas, las dos declaraciones son la misma función en lo que respecta a la sobrecarga, y obtienes un error de redefinición. Para ramificar según el tipo de resultado, cambia un parámetro (o usa plantillas / static_cast en el punto de llamada).

Cómo elige un ganador la resolución de sobrecarga

Cuando haces una llamada, el compilador clasifica todas las sobrecargas viables y elige la mejor coincidencia. A grandes rasgos, prefiere, en este orden:

  1. Una coincidencia exacta (sin conversión necesaria).
  2. Una promoción (p. ej. char o short -> int, float -> double).
  3. Una conversión estándar (p. ej. int -> double, double -> int, puntero a base).

Si exactamente una sobrecarga es estrictamente mejor que todas las demás, esa gana. Observa cómo una coincidencia exacta vence a una conversión:

'A' es un char, pero una promoción a int supera a una conversión a double, así que llama a la sobrecarga int. Estas reglas de clasificación son la razón por la que la resolución de sobrecarga normalmente "hace lo correcto", y de vez en cuando te sorprende.

La trampa de la ambigüedad

Si dos sobrecargas son igual de buenas (ninguna estrictamente mejor), el compilador se niega a adivinar y reporta una llamada ambigua. El caso de manual son dos sobrecargas que necesitan cada una una conversión del mismo rango:

void f(int x);
void f(double x);

f(0L);   // ERROR: ambigua - long -> int y long -> double son conversiones del mismo rango

Ni int ni double son una coincidencia exacta para long, y ambas conversiones están en el mismo rango, así que la llamada es ambigua. Tienes dos soluciones limpias:

Una sorpresa relacionada: pasar un literal de cadena. void g(const string&) y void g(bool) ambas pujarán por g("hi"), y bool puede ganar, porque un const char* se convierte a bool (no nulo -> true) con menos pasos que construir un std::string. Si alguna vez ves un literal de cadena llamando misteriosamente a tu sobrecarga bool, esta es la razón: añade una sobrecarga const char* o const string& para que tome la coincidencia exacta.

La sobrecarga y los argumentos por defecto no combinan bien

Los argumentos por defecto no son un sustituto de la sobrecarga, y combinar ambos crea ambigüedad. Cada uno puede responder a la misma llamada, así que el compilador no puede elegir:

void connect(string host, int port = 8080);   // se puede llamar con 1 argumento
void connect(string host);                     // también llamable con 1 argumento

connect("localhost");   // ERROR: ambigua - ambas coinciden con un solo argumento

Elige un enfoque por cada forma de llamada. Usa argumentos por defecto cuando el comportamiento es idéntico y solo quieres parámetros opcionales; usa sobrecarga cuando las distintas listas de argumentos deban ejecutar código genuinamente diferente. Mezclarlos de forma que dos firmas colisionen para el mismo número de argumentos es un error de ambigüedad garantizado.

Una distinción más que conviene fijar: la sobrecarga no es sobrescritura. La sobrecarga se resuelve en tiempo de compilación entre funciones del mismo ámbito con el mismo nombre pero distintos parámetros. La sobrescritura reemplaza una función virtual en una clase derivada en tiempo de ejecución y requiere la misma firma: un tema para las funciones virtuales más adelante.

A continuación: Lambdas

La sobrecarga le da a un nombre varias implementaciones tipadas elegidas en tiempo de compilación. Sin embargo, a veces no quieres una función con nombre en absoluto: necesitas una pequeña función desechable definida justo donde la usas, a menudo para pasarla a un algoritmo como sort. Eso es exactamente lo que son las lambdas: funciones anónimas que puedes escribir en línea, con las que capturas variables del entorno y entregas en una sola expresión. A continuación veremos cómo escribirlas y cuándo superan a una función con nombre completa.

Preguntas frecuentes

¿Qué es la sobrecarga de funciones en C++?

La sobrecarga de funciones te permite definir varias funciones con el mismo nombre siempre que sus listas de parámetros difieran (en número, tipo u orden). El compilador elige cuál llamar según los argumentos que le pasas, así que print(42) y print("hi") pueden invocar dos funciones print distintas.

¿Pueden dos funciones de C++ diferenciarse solo por el tipo de retorno?

No. Las sobrecargas deben diferir en su lista de parámetros. int f(int) y double f(int) son un error de compilación: el tipo de retorno no forma parte de la firma usada para la resolución de sobrecarga, porque el compilador elige la sobrecarga a partir de los argumentos en el punto de llamada, antes de usar siquiera el valor devuelto.

¿Qué provoca un error de "llamada ambigua" con funciones sobrecargadas?

Ocurre cuando dos sobrecargas son igual de buenas y el compilador no puede preferir una. Un caso clásico es f(int) y f(double) llamadas con f(0L) (un long), donde ambas requieren una conversión del mismo rango. Se soluciona añadiendo una sobrecarga de coincidencia exacta o con una conversión de tipos del argumento al tipo que quieres.

Coddy programming languages illustration

Aprende a programar con Coddy

COMENZAR