Sobrecargar una función C++ según el valor de retorno

Resuelto Motti asked hace 15 años • 16 respuestas

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.

Motti avatar Oct 22 '08 22:10 Motti
Aceptado

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

paercebal avatar Oct 23 '2008 08:10 paercebal
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.

Coincoin avatar Oct 22 '2008 15:10 Coincoin

Si quisieras que mulfuera 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 muluna 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;
}
Eclipse avatar Jan 09 '2009 21:01 Eclipse

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.

Zebra North avatar Oct 22 '2008 15:10 Zebra North