Sobrecarga por tipo de retorno

80

Leí algunas preguntas aquí sobre SO sobre este tema que me parece confuso. Recién comencé a aprender C ++ y aún no he estudiado las plantillas o la sobrecarga de operadores y demás.

Ahora, ¿hay una forma sencilla de sobrecargar

class My {
public:
    int get(int);
    char get(int);
}

sin plantillas o comportamiento extraño? o debería simplemente

class My {
public:
    int get_int(int);
    char get_char(int);
}

?

Zapato
fuente
1
@AdamV, me gusta mucho tu comentario. Corto pero completamente sólido.
Pouya
@Adam V En realidad, ya existe tal ambigüedad al tomar la dirección de una función sobrecargada. En tal caso, debería haber alguna expectativa de tipo de la expresión. En caso de que no haya uno, el programa está mal diseñado. Y esto ya está implementado. No creo que sea muy difícil usar las mismas reglas para implementar la sobrecarga de funciones por tipo de retorno. Entonces, en su ejemplo concreto, la ambigüedad se eliminará con un molde del tipo devuelto. La instancia con intvalor de retorno se verá así (int)get(9)y con charesto (char)get(9).
AnArrayOfFunctions
Cuando llegue aquí, creo que la mejor opción es pensar en dos nombres de funciones diferentes como lo sugiere Luchian.
Kemin Zhou

Respuestas:

100

No, no lo hay. No puede sobrecargar métodos basados ​​en el tipo de retorno.

La resolución de sobrecarga tiene en cuenta la firma de la función . Una firma de función se compone de:

  • nombre de la función
  • calificadores-cv
  • tipos de parámetros

Y aquí está la cita:

1.3.11 firma

la información sobre una función que participa en la resolución de sobrecarga (13.3): su lista de tipos de parámetros (8.3.5) y, si la función es un miembro de la clase, los calificadores cv (si los hay) en la función en sí y la clase en el que se declara la función miembro. [...]

Opciones:

1) cambie el nombre del método:

class My {
public:
    int getInt(int);
    char getChar(int);
};

2) parámetro de salida:

class My {
public:
    void get(int, int&);
    void get(int, char&);
}

3) plantillas ... exagerado en este caso.

Luchian Grigore
fuente
17
No puede sobrecargar una función normal en el tipo de retorno, pero el compilador elegirá entre los operadores de conversión en función del tipo resultante; puede aprovechar esto para crear un proxy que actúa efectivamente como si hubiera sobrecargado el tipo de retorno.
James Kanze
2
@JeffPigarelli Las plantillas miembro medios de solución de plantilla: My::get<T>(int). Es una alternativa válida _ si 1) tienes que manejar muchos tipos diferentes, todos con el mismo código básico (por ejemplo boost::lexical_cast<T>( someStringValue ), o tienes que poder llamar a estas funciones desde alguna otra plantilla ( myMy.get<T>( i ), donde Thay un argumento de esta otra plantilla . De lo contrario, como dice Luchian, son exagerados.
James Kanze
39
Tenga en cuenta que la razón por la que no puede sobrecargar según el tipo de retorno es que C ++ le permite descartar el valor de una llamada de función. Entonces, si simplemente llamara my.get(0);al compilador, no tendría forma de decidir qué pieza de código ejecutar.
benzado
9
@benzado en ese caso, se debe tirar errores de compilación sólo en estos casos, de lo contrario, debe deducir el tipo como lo hace en lo que muchos otros escenarios ya.
rr-
2
@benzado por el mismo razonamiento void foo(int x = 0) {} void foo(double x = 0) {}debería ser rechazado. Sin embargo, no lo es. Solo en el caso de que el compilador realmente no pueda distinguir ( foo()) obtendrá un error
larger_prime_is_463035818
82

Es posible, pero no estoy seguro de que sea una técnica que recomendaría a los principiantes. Como en otros casos, cuando desea que la elección de funciones dependa de cómo se usa el valor de retorno, usa un proxy; primero defina funciones como getChary getInt, luego un genérico get()que devuelve un Proxy como este:

class Proxy
{
    My const* myOwner;
public:
    Proxy( My const* owner ) : myOwner( owner ) {}
    operator int() const
    {
        return myOwner->getInt();
    }
    operator char() const
    {
        return myOwner->getChar();
    }
};

Extiéndalo a tantos tipos como necesite.

James Kanze
fuente
10
+1, aunque es un caso de esquina, el operador de conversión está sobrecargado en el tipo de devolución y se puede aprovechar para obtener esta función en todas partes.
Matthieu M.
3
@MatthieuM. Casi en todas partes, pero con las advertencias habituales sobre las conversiones implícitas. Corre el riesgo de introducir ambigüedades que de otro modo no existirían. Sin embargo, en el caso de un Proxy, creo que el riesgo es menor: no va a tener instancias del tipo Proxy excepto en el caso en el que desee la conversión implícita. Tenga en cuenta también que la conversión en el proxy cuenta como una conversión definida por el usuario. Si lo necesita std::string, y el proxy solo lo ofrece operator char const*(), no funcionará.
James Kanze
¿Por qué usar proxy aquí? No puedo pensar en ningún caso en el que el proxy sea obligatorio. ¿Puede proporcionar uno? ¡Gracias!
陳 力
8

No, no puede sobrecargar por tipo de retorno; solo por tipos de parámetros y calificadores const / volatile.

Una alternativa sería "regresar" usando un argumento de referencia:

void get(int, int&);
void get(int, char&);

aunque probablemente usaría una plantilla o funciones con nombres diferentes como su segundo ejemplo.

Mike Seymour
fuente
a la API de EFI donde el tipo de retorno es un intcódigo de error.
Cole Johnson
1
Tenga cuidado, los tipos char e int se pueden convertir implícitamente.
Kemin Zhou
5

Puedes pensar de esta manera:

Tienes:

  int get(int);
  char get(int);

Y no es obligatorio recopilar el valor de retorno de la función al invocar.

Ahora, invocas

  get(10);  -> there is an ambiguity here which function to invoke. 

Por lo tanto, no tiene sentido si se permite la sobrecarga según el tipo de retorno.

Quién soy
fuente
4

Resucitando un hilo antiguo, pero puedo ver que nadie mencionó la sobrecarga de los calificadores de referencia. Los calificadores de referencia son una característica del lenguaje agregada en C ++ 11 y recientemente me encontré con ella; no está tan extendida como, por ejemplo, los calificadores de cv. La idea principal es distinguir entre los dos casos: cuando la función miembro se llama en un objeto rvalue y cuando se llama en un objeto lvalue. Básicamente, puedes escribir algo como esto (estoy modificando ligeramente el código de OP):

#include <stdio.h>

class My {
public:
    int get(int) & { // notice &
        printf("returning int..\n");
        return 42;
    }
    char get(int) && { // notice &&
        printf("returning char..\n");
        return 'x';
    };
};

int main() {
    My oh_my;
    oh_my.get(13); // 'oh_my' is an lvalue
    My().get(13); // 'My()' is a temporary, i.e. an rvalue
}

Este código producirá el siguiente resultado:

returning int..
returning char..

Por supuesto, como es el caso de los calificadores cv, ambas funciones podrían haber devuelto el mismo tipo y la sobrecarga aún sería exitosa.

Miljen Mikic
fuente
4

Como se dijo antes, las plantillas son exageradas en este caso, pero sigue siendo una opción que vale la pena mencionar.

class My {
public:
    template<typename T> T get(int);
};

template<> int My::get<int>(int);
template<> char My::get<char>(int);
fabda01
fuente
3

Si bien la mayoría de los otros comentarios sobre este problema son técnicamente correctos, puede sobrecargar efectivamente el valor de retorno si lo combina con el parámetro de entrada de sobrecarga. Por ejemplo:

class My {
public:
    int  get(int);
    char get(unsigned int);
};

MANIFESTACIÓN:

#include <stdio.h>

class My {
public:
    int  get(         int x) { return 'I';  };
    char get(unsinged int x) { return 'C';  };
};

int main() {

    int i;
    My test;

    printf( "%c\n", test.get(               i) );
    printf( "%c\n", test.get((unsigned int) i) );
}

El resultado de esto es:

I 
C
códigochimp
fuente
6
que cambia por completo la firma de la función para que no se sobrecargue por tipo de retorno, simplemente se sobrecargue
Dado
Utilicé este método con éxito para devolver una variedad de valores para una producción en ejecución C ++ JSON API. ¡Funcionó muy bien! Aunque técnicamente no se sobrecarga por tipo de retorno, logra la intención de diferentes tipos de retorno con el mismo nombre de función, es C ++ válido y claro, y tiene poca sobrecarga (una instanciación de una sola variable en la llamada de función).
guidotex
Aunque esto no es una sobrecarga por tipo de retorno, funciona. me hace decir "astuto"
Marcus
2

No hay forma de sobrecargar por tipo de retorno en C ++. Sin usar plantillas, usar get_inty get_charserá lo mejor que pueda hacer.

sepp2k
fuente
Solo para estar seguro: ¿algo como template <class T> T get(int)funcionaría?
Niklas B.
4
Sí, @Niklas, pero tendrías que llamarlo como get<int>o get<char>, lo que realmente no te hace ganar mucho get_inty get_charsi no estás usando también otras funciones de plantilla.
Rob Kennedy
@Rob: Bueno, el compilador puede determinar Tsi tiene algo como T get(T). Si llama get('a'), el compilador deduce que Tes un chary no tiene que llamar explícitamente get<char>('a'). Todavía no estoy seguro de si esto es estándar, aunque creo que lo es. Para su información, tanto GCC como Clang lo admiten.
netcoder
1
Eso es perfectamente estándar, @Netcoder, pero no se trata de que el compilador deduzca solo el tipo de retorno, que sugirió que era posible. En su ejemplo, el compilador deduce el tipo de argumento y, una vez que lo sabe, completa el valor de Tcualquier otro lugar, incluido el tipo de retorno. Esperaba que dieras un ejemplo del compilador deduciendo Tde la función en el primer comentario de Niklas.
Rob Kennedy
2

No puede sobrecargar métodos basados ​​en tipos de devolución. Su mejor opción es crear dos funciones con una sintaxis ligeramente diferente, como en su segundo fragmento de código.

Pecoso
fuente
0

no se puede sobrecargar una función en función del tipo de retorno de la función. puede sobrepasar según el tipo y la cantidad de argumentos que toma esta función.

AlexDan
fuente
0

Usé la respuesta de James Kanze usando un proxy:

https://stackoverflow.com/a/9569120/262458

Quería evitar usar muchos static_casts feos en un void *, así que hice esto:

#include <SDL_joystick.h>
#include <SDL_gamecontroller.h>

struct JoyDev {
    private:
        union {
            SDL_GameController* dev_gc = nullptr;
            SDL_Joystick*       dev_js;
        };
    public:
        operator SDL_GameController*&() { return dev_gc; }
        operator SDL_Joystick*&()       { return dev_js; }

        SDL_GameController*& operator=(SDL_GameController* p) { dev_gc = p; return dev_gc; }
        SDL_Joystick*&       operator=(SDL_Joystick* p)       { dev_js = p; return dev_js; }
};

struct JoyState {
    public:
        JoyDev dev;
};

int main(int argc, char** argv)
{
    JoyState js;

    js.dev = SDL_JoystickOpen(0);

    js.dev = SDL_GameControllerOpen(0);

    SDL_GameControllerRumble(js.dev, 0xFFFF, 0xFFFF, 300);

    return 0;
}

¡Funciona perfectamente!

Rafael Kitover
fuente