Überladung nach Rückgabetyp

80

Ich habe hier auf SO einige Fragen zu diesem Thema gelesen, die mir noch verwirrend erscheinen. Ich habe gerade angefangen, C ++ zu lernen, und ich habe noch keine Vorlagen oder Operatorüberladung und so weiter studiert.

Jetzt gibt es eine einfache Möglichkeit zur Überlastung

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

ohne Vorlagen oder seltsames Verhalten? oder sollte ich nur

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

?

Schuh
quelle
3
Mögliches Duplikat der Funktionsüberladung nach Rückgabetyp?
1
@AdamV, ich mag deinen Kommentar wirklich. Kurz aber ganz fest.
Pouya
@Adam V Tatsächlich besteht bereits eine solche Unklarheit darin, die Adresse einer überladenen Funktion zu übernehmen. In diesem Fall sollte eine Typerwartung des Ausdrucks vorliegen. Falls es keine gibt, ist das Programm schlecht geformt. Und das ist bereits umgesetzt. Ich denke nicht, dass es sehr schwierig sein wird, dieselben Regeln für die Implementierung der Funktionsüberladung nach Rückgabetyp zu verwenden. In Ihrem konkreten Beispiel wird die Mehrdeutigkeit mit einem Cast des zurückgegebenen Typs beseitigt. Die Instanzierung mit dem intRückgabewert sieht so (int)get(9)und charso aus (char)get(9).
AnArrayOfFunctions
Wenn Sie hier ankommen, ist es meiner Meinung nach die beste Wahl, sich zwei verschiedene Funktionsnamen vorzustellen, wie sie von Luchian vorgeschlagen wurden.
Kemin Zhou

Antworten:

98

Nein, gibt es nicht. Sie können Methoden basierend auf dem Rückgabetyp nicht überladen.

Die Überlastungsauflösung berücksichtigt die Funktionssignatur . Eine Funktionssignatur besteht aus:

  • Funktionsname
  • Lebenslauf-Qualifikanten
  • Parametertypen

Und hier ist das Zitat:

1.3.11 Unterschrift

die Informationen zu einer Funktion, die an der Überlastungsauflösung beteiligt ist (13.3): ihre Parametertypliste (8.3.5) und, falls die Funktion ein Klassenmitglied ist, die Lebenslaufqualifizierer (falls vorhanden) für die Funktion selbst und die Klasse in dem die Mitgliedsfunktion deklariert ist. [...]

Optionen:

1) Ändern Sie den Methodennamen:

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

2) out Parameter:

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

3) Vorlagen ... in diesem Fall übertrieben.

Luchian Grigore
quelle
17
Sie können eine normale Funktion für den Rückgabetyp nicht überladen, aber der Compiler wählt basierend auf dem resultierenden Typ zwischen Konvertierungsoperatoren aus. Sie können dies nutzen, um einen Proxy zu erstellen, der sich so verhält, als wären Sie beim Rückgabetyp überlastet.
James Kanze
2
@ JeffPigarelli Die Vorlagenlösung bedeutet Mitgliedsvorlagen : My::get<T>(int). Es ist eine gültige Alternative _wenn 1) Sie müssen mit vielen verschiedenen Typen umgehen, alle mit demselben Basiscode (z. B. boost::lexical_cast<T>( someStringValue )oder Sie müssen in der Lage sein, diese Funktionen aus einer anderen Vorlage aufzurufen ( myMy.get<T>( i )wo Tist ein Argument dieser anderen Vorlage)? Ansonsten, wie Luchian sagt, sind sie übertrieben.
James Kanze
39
Man beachte , dass der Grund , Sie auf Rückgabetyp nicht Überlastung basiert kann ist , dass C ++ können Sie den Wert eines Funktionsaufrufs zu verwerfen. Wenn Sie also einfach my.get(0);den Compiler aufrufen, können Sie nicht entscheiden, welcher Code ausgeführt werden soll.
Benzado
9
@benzado In diesem Fall sollte es nur in solchen Fällen Compilerfehler auslösen. Andernfalls sollte der Typ abgeleitet werden, wie dies bereits in so vielen anderen Szenarien der Fall ist .
rr-
2
@benzado für die gleiche Argumentation void foo(int x = 0) {} void foo(double x = 0) {}sollte nicht erlaubt sein. Dies ist jedoch nicht der Fall. Nur in dem Fall, in dem der Compiler wirklich nicht unterscheiden kann ( foo()), erhalten Sie eine Fehlermeldung
idclev 463035818
82

Es ist möglich, aber ich bin nicht sicher, ob es eine Technik ist, die ich Anfängern empfehlen würde. Wie in anderen Fällen verwenden Sie einen Proxy, wenn die Auswahl der Funktionen von der Verwendung des Rückgabewerts abhängen soll. Definieren Sie zuerst Funktionen wie getCharund getIntdann ein Generikum, get()das einen Proxy wie folgt zurückgibt:

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

Erweitern Sie es auf so viele Typen, wie Sie benötigen.

James Kanze
quelle
10
+1, obwohl ein Eckfall, ist der Konvertierungsoperator beim Rückgabetyp tatsächlich überlastet und kann genutzt werden, um diese Funktion überall zu nutzen.
Matthieu M.
3
@MatthieuM. Überall, aber mit den üblichen Einschränkungen bezüglich impliziter Konvertierungen. Sie laufen Gefahr, Unklarheiten einzuführen, die sonst nicht vorhanden wären. Im Fall eines Proxys ist das Risiko jedoch meiner Meinung nach gering - Sie werden keine anderen Instanzen des Proxy-Typs haben als in dem Fall, in dem Sie die implizite Konvertierung wünschen. Beachten Sie auch, dass die Konvertierung im Proxy als eine benutzerdefinierte Konvertierung zählt. Wenn Sie brauchen std::stringund der Proxy nur bietet operator char const*(), wird es nicht funktionieren.
James Kanze
Warum hier Proxy verwenden? Ich kann mir keine Fälle vorstellen, in denen Proxy ein Muss ist. Können Sie eine zur Verfügung stellen? Vielen Dank!
力 力
8

Nein, Sie können nicht nach Rückgabetyp überladen. Nur nach Parametertypen und const / volatile-Qualifizierern.

Eine Alternative wäre, mit einem Referenzargument "zurückzukehren":

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

obwohl ich wahrscheinlich entweder eine Vorlage oder anders benannte Funktionen wie Ihr zweites Beispiel verwenden würde.

Mike Seymour
quelle
a la EFI-API, wobei der Rückgabetyp ein intFehlercode ist.
Cole Johnson
1
Seien Sie vorsichtig, char- und int-Typen können implizit konvertiert werden.
Kemin Zhou
5

Sie können so denken:

Du hast:

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

Es ist nicht zwingend erforderlich, den Rückgabewert der Funktion beim Aufrufen zu erfassen.

Jetzt rufst du an

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

Keine Bedeutung, wenn eine Überladung basierend auf dem Rückgabetyp zulässig ist.

Wer bin ich
quelle
4

Wiederbelebung eines alten Threads, aber ich kann sehen, dass niemand die Überladung durch Ref-Qualifier erwähnte. Ref-Qualifizierer sind eine Sprachfunktion, die in C ++ 11 hinzugefügt wurde, und ich bin erst kürzlich darauf gestoßen - sie ist nicht so weit verbreitet wie z. B. Lebenslaufqualifizierer. Die Hauptidee besteht darin, zwischen den beiden Fällen zu unterscheiden: Wenn die Elementfunktion für ein rvalue-Objekt aufgerufen wird und wenn sie für ein lvalue-Objekt aufgerufen wird. Sie können im Grunde so etwas schreiben (ich ändere den OP-Code leicht):

#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
}

Dieser Code erzeugt die folgende Ausgabe:

returning int..
returning char..

Natürlich könnten, wie es bei Lebenslaufqualifizierern der Fall ist, beide Funktionen den gleichen Typ zurückgegeben haben, und eine Überladung wäre immer noch erfolgreich.

Miljen Mikic
quelle
3

Während die meisten anderen Kommentare zu diesem Problem technisch korrekt sind, können Sie den Rückgabewert effektiv überladen, wenn Sie ihn mit dem Überladen von Eingabeparametern kombinieren. Zum Beispiel:

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

DEMO:

#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) );
}

Das Ergebnis davon ist:

I 
C
Codechimp
quelle
6
Das ändert die Funktionssignatur vollständig, so dass Sie nicht nach Rückgabetyp überladen werden. Sie überladen einfach
Dado
Ich habe diese Methode erfolgreich verwendet, um eine Vielzahl von Werten für eine C ++ JSON-API zurückzugeben, auf der die Produktion ausgeführt wird. Hat super funktioniert! Obwohl es technisch nicht nach Rückgabetyp überladen ist, erreicht es die Absicht verschiedener Rückgabetypen mit demselben Funktionsnamen, ist in C ++ gültig und klar und hat wenig Overhead (eine einzelne Variableninstanziierung im Funktionsaufruf).
Guidotex
Obwohl dies nicht durch den Rückgabetyp überladen wird, erledigt es den Job. lässt mich "hinterhältig" sagen
Marcus
2

In C ++ gibt es keine Möglichkeit, den Rückgabetyp zu überladen. Ohne Vorlagen zu verwenden, ist get_intund get_chardas Beste, was Sie tun können.

sepp2k
quelle
Nur um sicher zu gehen: So etwas template <class T> T get(int)würde funktionieren?
Niklas B.
4
Ja, @Niklas, aber Sie müssten es als get<int>oder bezeichnen get<char>, was Sie nicht wirklich überzeugt get_intund get_charwenn Sie nicht auch andere Vorlagenfunktionen verwenden.
Rob Kennedy
@Rob: Nun, der Compiler kann feststellen, Tob Sie so etwas haben T get(T). Wenn Sie aufrufen get('a'), leitet der Compiler ab, dass dies ein Tist, charund Sie müssen nicht explizit aufrufen get<char>('a'). Ich bin mir immer noch nicht sicher, ob dies Standard ist, obwohl ich denke, dass dies der Fall ist. Zu Ihrer Information, sowohl GCC als auch Clang unterstützen dies.
Netcoder
1
Das ist vollkommen Standard, @Netcoder, aber es geht nicht darum, dass der Compiler nur den Rückgabetyp ableitet, den Sie für möglich gehalten haben. In Ihrem Beispiel leitet der Compiler den Argumenttyp ab, und sobald er dies weiß, gibt er den Wert von Tüberall sonst ein, einschließlich des Rückgabetyps. Ich habe erwartet, dass Sie Tin Niklas 'erstem Kommentar ein Beispiel für den Compiler geben, der für die Funktion abgeleitet wurde.
Rob Kennedy
2

Sie können Methoden, die auf Rückgabetypen basieren, nicht überladen. Am besten erstellen Sie zwei Funktionen mit leicht unterschiedlicher Syntax, z. B. in Ihrem zweiten Codefragment.

Sommersprossenfuß
quelle
2

Wie bereits erwähnt, sind Vorlagen in diesem Fall übertrieben, aber es ist immer noch eine erwähnenswerte Option.

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

template<> int My::get<int>(int);
template<> char My::get<char>(int);
fabda01
quelle
0

Sie können eine Funktion basierend auf dem Rückgabetyp der Funktion nicht überladen. Sie können basierend auf dem Typ und der Anzahl der Argumente, die diese Funktion verwendet, überlagern.

AlexDan
quelle
0

Ich habe James Kanzes Antwort mit einem Proxy verwendet:

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

Ich wollte vermeiden, viele hässliche static_casts für eine Leere * zu verwenden, also habe ich Folgendes getan:

#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;
}

Funktioniert perfekt!

Rafael Kitover
quelle