Menu

Sobrecarga de funções em C++: mesmo nome, parâmetros diferentes

A sobrecarga de funções em C++ permite que várias funções compartilhem um único nome, desde que suas listas de parâmetros sejam diferentes. Aprenda como a resolução de sobrecarga escolhe a versão certa, por que o tipo de retorno sozinho não conta e as armadilhas de ambiguidade e argumentos padrão que você deve evitar.

Esta página tem editores executáveis - edite, execute e veja a saída na hora.

Um nome, muitas versões

Muitas vezes você precisa da mesma operação para tipos de dados diferentes: imprimir um int, imprimir uma string, imprimir um double. Em algumas linguagens você inventaria printInt, printString, printDouble. C++ permite dar a todas o mesmo nome e as distingue pelos parâmetros. Isso é sobrecarga de funções.

A regra é simples: várias funções podem compartilhar um nome, desde que suas listas de parâmetros sejam diferentes - no número de parâmetros, em seus tipos ou em sua ordem. O compilador analisa os argumentos em cada ponto de chamada e escolhe para você a versão correspondente.

Três funções, um nome. Cada chamada cai na versão cujo tipo de parâmetro combina com o argumento. É isso que faz std::cout << x funcionar igualmente para ints, doubles e strings - operator<< é sobrecarregado muitas e muitas vezes.

O que conta como uma sobrecarga diferente

Uma sobrecarga é distinguida apenas pela sua lista de parâmetros. Você pode variar:

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

void log(string msg, int level);     // a ordem importa...
void log(int level, string msg);     // ...então esta também é diferente

Cada uma dessas é uma sobrecarga legal e distinta. O compilador monta um conjunto de candidatas a partir de todas as funções chamadas area e depois faz a correspondência por número e tipo de argumentos.

O tipo de retorno sozinho não basta

Aqui está a armadilha que faz quase todo mundo tropeçar: você não pode sobrecarregar pelo tipo de retorno. O valor de retorno não tem papel na escolha da sobrecarga, porque o compilador decide qual função chamar a partir dos argumentos - antes mesmo de olhar o que é devolvido.

int    convert(double x);   // OK
double convert(double x);   // ERRO: redefinição - só o tipo de retorno difere

Isso não compila. Se as listas de parâmetros são idênticas, as duas declarações são a mesma função no que diz respeito à sobrecarga, e você recebe um erro de redefinição. Para ramificar conforme o tipo do resultado, mude um parâmetro (ou use templates / uma conversão de tipo com static_cast no ponto de chamada).

Como a resolução de sobrecarga escolhe um vencedor

Quando você faz uma chamada, o compilador classifica todas as sobrecargas viáveis e escolhe a melhor correspondência. De modo geral, ele prefere, nesta ordem:

  1. Uma correspondência exata (sem conversão necessária).
  2. Uma promoção (ex.: char ou short -> int, float -> double).
  3. Uma conversão padrão (ex.: int -> double, double -> int, ponteiro para a base).

Se exatamente uma sobrecarga é estritamente melhor que todas as outras, essa vence. Veja como uma correspondência exata supera uma conversão:

'A' é um char, mas uma promoção para int supera uma conversão para double, então ela chama a sobrecarga int. Essas regras de classificação são o motivo de a resolução de sobrecarga normalmente "fazer a coisa certa" - e, ocasionalmente, surpreender você.

A armadilha da ambiguidade

Se duas sobrecargas são igualmente boas - nenhuma estritamente melhor - o compilador se recusa a adivinhar e reporta uma chamada ambígua. O caso clássico são duas sobrecargas que cada uma precisa de uma conversão de mesmo nível:

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

f(0L);   // ERRO: ambígua - long -> int e long -> double são conversões de mesmo nível

Nem int nem double são correspondência exata para long, e ambas as conversões ficam no mesmo nível, então a chamada é ambígua. Você tem duas soluções limpas:

Uma surpresa relacionada: passar um literal de string. void g(const string&) e void g(bool) ambas vão disputar g("hi"), e bool pode vencer, porque um const char* converte para bool (não nulo -> true) com menos passos do que construir um std::string. Se algum dia você vir um literal de string misteriosamente chamando sua sobrecarga bool, é por isso - adicione uma sobrecarga const char* ou const string& para que ela tome a correspondência exata.

Sobrecarga e argumentos padrão não combinam bem

Argumentos padrão não são um substituto para a sobrecarga, e combinar os dois cria ambiguidade. Cada um pode responder à mesma chamada, então o compilador não consegue escolher:

void connect(string host, int port = 8080);   // pode ser chamada com 1 argumento
void connect(string host);                     // também chamável com 1 argumento

connect("localhost");   // ERRO: ambígua - ambas correspondem a um único argumento

Escolha uma abordagem por forma de chamada. Use argumentos padrão quando o comportamento é idêntico e você só quer parâmetros opcionais; use sobrecarga quando as listas de argumentos diferentes devem executar código genuinamente diferente. Misturá-los de modo que duas assinaturas colidam para a mesma quantidade de argumentos é um erro de ambiguidade garantido.

Mais uma distinção que vale a pena fixar: sobrecarga não é sobrescrita. A sobrecarga é resolvida em tempo de compilação entre funções no mesmo escopo com o mesmo nome mas parâmetros diferentes. A sobrescrita substitui uma função virtual em uma classe derivada em tempo de execução e exige a mesma assinatura - um tema para as funções virtuais mais adiante.

A seguir: Lambdas

A sobrecarga dá a um nome várias implementações tipadas escolhidas em tempo de compilação. Às vezes, porém, você não quer uma função nomeada de jeito nenhum - você precisa de uma pequena função descartável definida bem onde ela é usada, muitas vezes para passar a um algoritmo como sort. É exatamente isso que são as lambdas: funções anônimas que você pode escrever inline, com as quais captura variáveis ao redor e entrega em uma única expressão. A seguir veremos como escrevê-las e quando elas superam uma função nomeada completa.

Perguntas frequentes

O que é sobrecarga de funções em C++?

A sobrecarga de funções permite definir várias funções com o mesmo nome, desde que suas listas de parâmetros sejam diferentes (em número, tipo ou ordem). O compilador escolhe qual chamar com base nos argumentos que você passa, então print(42) e print("hi") podem chamar duas funções print diferentes.

Duas funções em C++ podem diferir apenas pelo tipo de retorno?

Não. As sobrecargas precisam diferir em sua lista de parâmetros. int f(int) e double f(int) causam um erro de compilação - o tipo de retorno não faz parte da assinatura usada na resolução de sobrecarga, porque o compilador escolhe a sobrecarga a partir dos argumentos no ponto de chamada, antes mesmo de o valor de retorno ser usado.

O que causa um erro de "chamada ambígua" com funções sobrecarregadas?

Acontece quando duas sobrecargas são igualmente boas e o compilador não consegue preferir uma. Um caso clássico é f(int) e f(double) chamadas com f(0L) (um long), em que ambas exigem uma conversão de mesmo nível. Corrija adicionando uma sobrecarga de correspondência exata ou convertendo o argumento para o tipo que você quer.

Coddy programming languages illustration

Aprenda a programar com o Coddy

COMEÇAR