Sobrecargar una función C++ según el valor de retorno
Todos sabemos que puedes sobrecargar una función según los parámetros:
int mul(int i, int j) { return i*j; }
std::string mul(char c, int n) { return std::string(n, c); }
¿Se puede sobrecargar una función según el valor de retorno? Defina una función que devuelva diferentes cosas según cómo se utilice el valor de retorno:
int n = mul(6, 3); // n = 18
std::string s = mul(6, 3); // s = "666"
// Note that both invocations take the exact same parameters (same types)
Puede asumir que el primer parámetro está entre 0 y 9, no es necesario verificar la entrada ni realizar ningún manejo de errores.
Tienes que decirle al compilador qué versión usar. En C++, puedes hacerlo de tres maneras.
Diferenciar explícitamente las llamadas escribiendo
Hiciste un poco de trampa porque enviaste un número entero a una función esperando un carácter y enviaste erróneamente el número seis cuando el valor del carácter '6' no es 6 sino 54 (en ASCII):
std::string mul(char c, int n) { return std::string(n, c); }
std::string s = mul(6, 3); // s = "666"
La solución correcta sería, por supuesto,
std::string s = mul(static_cast<char>(54), 3); // s = "666"
Supongo que vale la pena mencionar esto, incluso si no quisieras la solución.
Diferenciar explícitamente las llamadas por puntero ficticio
Puede agregar un parámetro ficticio a cada función, lo que obligará al compilador a elegir las funciones correctas. La forma más sencilla es enviar un puntero ficticio NULL del tipo deseado para la devolución:
int mul(int *, int i, int j) { return i*j; }
std::string mul(std::string *, char c, int n) { return std::string(n, c); }
Que se puede utilizar con el código:
int n = mul((int *) NULL, 6, 3); // n = 18
std::string s = mul((std::string *) NULL, 54, 3); // s = "666"
Diferenciar explícitamente las llamadas mediante la creación de plantillas para el valor de retorno
Con esta solución, creamos una función "ficticia" con código que no se compilará si se crea una instancia:
template<typename T>
T mul(int i, int j)
{
// If you get a compile error, it's because you did not use
// one of the authorized template specializations
const int k = 25 ; k = 36 ;
}
Notarás que esta función no se compila, lo cual es bueno porque solo queremos usar algunas funciones limitadas a través de la especialización de plantillas:
template<>
int mul<int>(int i, int j)
{
return i * j ;
}
template<>
std::string mul<std::string>(int i, int j)
{
return std::string(j, static_cast<char>(i)) ;
}
Así, se compilará el siguiente código:
int n = mul<int>(6, 3); // n = 18
std::string s = mul<std::string>(54, 3); // s = "666"
Pero este no lo hará:
short n2 = mul<short>(6, 3); // error: assignment of read-only variable ‘k’
Diferenciar explícitamente las llamadas mediante la plantilla del valor de retorno, 2
¡Oye, tú también hiciste trampa!
Bien, utilicé los mismos parámetros para las dos funciones "sobrecargadas". Pero tú empezaste a hacer trampa (ver arriba)...
^_^
Más en serio, si necesita tener diferentes parámetros, entonces deberá escribir más código y luego tendrá que usar explícitamente los tipos correctos al llamar a las funciones para evitar ambigüedades:
// For "int, int" calls
template<typename T>
T mul(int i, int j)
{
// If you get a compile error, it's because you did not use
// one of the authorized template specializations
const int k = 25 ; k = 36 ;
}
template<>
int mul<int>(int i, int j)
{
return i * j ;
}
// For "char, int" calls
template<typename T>
T mul(char i, int j)
{
// If you get a compile error, it's because you did not use
// one of the authorized template specializations
const int k = 25 ; k = 36 ;
}
template<>
std::string mul<std::string>(char i, int j)
{
return std::string(j, (char) i) ;
}
Y este código se usaría como tal:
int n = mul<int>(6, 3); // n = 18
std::string s = mul<std::string>('6', 3); // s = "666"
Y la siguiente línea:
short n2 = mul<short>(6, 3); // n = 18
Todavía no se compilaría.
Conclusión
Me encanta C++...
:-pag
class mul
{
public:
mul(int p1, int p2)
{
param1 = p1;
param2 = p2;
}
operator int ()
{
return param1 * param2;
}
operator std::string ()
{
return std::string(param2, param1 + '0');
}
private:
int param1;
int param2;
};
No es que usaría eso.
Si quisieras que mul
fuera una función real en lugar de una clase, podrías usar una clase intermedia:
class StringOrInt
{
public:
StringOrInt(int p1, int p2)
{
param1 = p1;
param2 = p2;
}
operator int ()
{
return param1 * param2;
}
operator std::string ()
{
return std::string(param2, param1 + '0');
}
private:
int param1;
int param2;
};
StringOrInt mul(int p1, int p2)
{
return StringOrInt(p1, p2);
}
Esto le permite hacer cosas como pasar mul
una función a algoritmos estándar:
int main(int argc, char* argv[])
{
vector<int> x;
x.push_back(3);
x.push_back(4);
x.push_back(5);
x.push_back(6);
vector<int> intDest(x.size());
transform(x.begin(), x.end(), intDest.begin(), bind1st(ptr_fun(&mul), 5));
// print 15 20 25 30
for (vector<int>::const_iterator i = intDest.begin(); i != intDest.end(); ++i)
cout << *i << " ";
cout << endl;
vector<string> stringDest(x.size());
transform(x.begin(), x.end(), stringDest.begin(), bind1st(ptr_fun(&mul), 5));
// print 555 5555 55555 555555
for (vector<string>::const_iterator i = stringDest.begin(); i != stringDest.end(); ++i)
cout << *i << " ";
cout << endl;
return 0;
}
No.
No puede sobrecargar por valor de retorno porque la persona que llama puede hacer cualquier cosa (o nada) con él. Considerar:
mul(1, 2);
El valor de retorno simplemente se desecha, por lo que no hay forma de elegir una sobrecarga basándose únicamente en el valor de retorno.