Sobrecarga de función basada en el valor frente a la referencia de Const

¿Declar algo como el siguiente

void foo(int x) { std::cout << "foo(int)" << std::endl; } void foo(const int &x) { std::cout << "foo(const int &)" << std::endl; } 

alguna vez tiene sentido? ¿Cómo podría la persona que llama ser capaz de diferenciar entre ellos? He intentado

 foo(9); // Compiler complains ambiguous call. int x = 9; foo(x); // Also ambiguous. const int &y = x; foo(y); // Also ambiguous. 

La intención parece ser la de diferenciar entre invocaciones con temporales (es decir, 9 ) y el paso de argumentos “regulares”. El primer caso puede permitir que la implementación de la función emplee optimizaciones, ya que está claro que los argumentos se eliminarán después (lo cual es absolutamente absurdo para los literales enteros, pero puede tener sentido para los objetos definidos por el usuario).

Sin embargo, el estándar de lenguaje C ++ actual no ofrece una forma de sobrecargar específicamente para el ‘l / r-valueness’ de los argumentos: cualquier l-value que se transmita como argumento a una función se puede convertir implícitamente a una referencia, por lo que la ambigüedad es inevitable.

C ++ 11 presenta una nueva herramienta para un propósito similar: al usar referencias de valores r , puede sobrecargar de la siguiente manera

 void foo(int x) { ... } void foo(const int &&x) { ... } 

… y foo(4) (un valor r temporal pasado como argumento) haría que el comstackdor escogiera la segunda sobrecarga mientras int i = 2; foo(i) int i = 2; foo(i) elegiría el primero.

( Nota : ¡incluso con la nueva cadena de herramientas, no es posible diferenciar entre los casos 2 y 3 en su muestra!)

Puedes hacer esto con una plantilla:

template void foo(T x) { ... }

Luego puede llamar a esta plantilla por valor o por referencia:

 int x = 123; foo(x); // by value foo(x); // by refernce 

¿Cómo podría la persona que llama ser capaz de diferenciar entre ellos?

No se puede diferenciar en este caso. Ambas funciones sobrecargadas tienen el mismo tipo de tipo de datos primitivos que el argumento. Y tomar por referencia no cuenta para un tipo diferente.

El comstackdor no puede. Ambas definiciones de foo se pueden usar para todas las ‘variantes’ de int.

En el primer foo, se hace una copia del int. Copiar un int es siempre posible.

En el segundo foo, se pasa una referencia a const int. Dado que cualquier int se puede convertir en un const int, también se puede pasar una referencia a él.

Como ambas variantes son válidas en todos los casos, el comstackdor no puede elegir.

Las cosas se vuelven diferentes si, por ejemplo, usa la siguiente definición:

 void foo (int &x); 

Ahora llamarlo con foo(9) tomará la primera alternativa, ya que no puede pasar 9 como una referencia int sin const.

Otro ejemplo, si reemplaza int por una clase donde el constructor de copia es privado, entonces la persona que llama no puede hacer una copia del valor, y la primera variante foo no será utilizada.

No en C ++. Los lenguajes funcionales como Erlang y Haskell se acercan al permitirle especificar sobrecargas de función basadas en el valor del parámetro, pero la mayoría de los lenguajes imperativos, incluido C ++, requieren sobrecarga según la firma del método; es decir, el número y tipo de cada parámetro y el tipo del valor de retorno.

La palabra clave const en la firma no define el tipo del parámetro, sino su mutabilidad dentro de la función; un parámetro ” const ” generará un error de comstackción si la función lo modifica o lo pasa por referencia a cualquier función que no use también const .