268 Stimmen

Funktionsüberladung nach Rückgabetyp?

Warum unterstützen nicht mehr gängige statisch typisierte Sprachen das Überladen von Funktionen/Methoden nach Rückgabetyp? Ich kann mich an keine erinnern, die das tut. Es scheint nicht weniger nützlich oder sinnvoll zu sein als die Unterstützung von Überladung nach Parametertyp. Wie kommt es, dass es so viel weniger populär ist?

2 Stimmen

0 Stimmen

@user195488 dies ist kein Duplikat, weil es eine allgemeine Frage ist.

37voto

Frederick The Fool Punkte 33140

Wenn Funktionen durch den Rückgabetyp überladen würden und Sie diese beiden Überladungen hätten

int func();
string func();

kann der Compiler auf keinen Fall herausfinden, welche der beiden Funktionen er bei einem Aufruf wie diesem aufrufen soll

void main() 
{
    func();
}

Aus diesem Grund verbieten die Entwickler von Sprachen oft die Überladung von Rückgabewerten.

Einige Sprachen (wie MSIL) hingegen, tun Überladen nach Rückgabetyp erlauben. Auch sie stehen natürlich vor der oben genannten Schwierigkeit, aber sie haben Umgehungsmöglichkeiten, für die Sie ihre Dokumentation konsultieren müssen.

5 Stimmen

Eine kleine Spitzfindigkeit (Ihre Antwort gibt eine sehr klare, verständliche Begründung): Es ist nicht so, dass es keinen Weg gibt; es ist nur so, dass die Wege umständlich und schmerzhafter wären, als es den meisten Menschen lieb ist. In C++ zum Beispiel wäre die Überladung wahrscheinlich mit einer hässlichen Cast-Syntax auflösbar gewesen.

0 Stimmen

Aber Michael, wie könnte ein Aufruf wie der obige (func();) aufgelöst werden, ohne dass der Benutzer selbst dem Compiler mit zusätzlichen Hinweisen hilft? Ich glaube nicht, dass das überhaupt möglich ist.

0 Stimmen

Der Rückgabewert wird nirgends verwendet, so dass es keine Rolle spielt, welche der beiden Funktionen aufgerufen wird. Tatsächlich ist Ihre gesamte main()-Funktion ein No-op.

29voto

Greg Hewgill Punkte 882617

Wie würden Sie in einer solchen Sprache die folgenden Fragen lösen?

f(g(x))

wenn f hatte Überlastungen void f(int) y void f(string) y g hatte Überlastungen int g(int) y string g(int) ? Sie bräuchten eine Art Disambiguator.

Ich denke, in den Situationen, in denen Sie dies benötigen, wäre es besser, einen neuen Namen für die Funktion zu wählen.

2 Stimmen

Auch die reguläre Art der Überladung kann zu Mehrdeutigkeiten führen. Ich denke, diese werden in der Regel durch Zählen der Anzahl von Würfen erforderlich gelöst, aber das funktioniert nicht immer.

1 Stimmen

Ja, Standardkonvertierungen werden in exakte Übereinstimmung, Beförderung und Konvertierung eingeteilt: void f(int); void f(long); f('a'); ruft f(int) auf, weil das nur eine Beförderung ist, während die Konvertierung in long eine Konvertierung ist. void f(float); void f(short); f(10); würde für beide eine Konvertierung erfordern: der Aufruf ist zweideutig.

0 Stimmen

Wenn die Sprache über eine faule Auswertung verfügt, ist dies kein so großes Problem.

22voto

Michael Burr Punkte 320591

Um eine C++-spezifische Lösung zu stehlen Antwort auf eine andere, sehr ähnliche Frage (Duplikat?):


Die Rückgabetypen von Funktionen spielen bei der Überladungsauflösung keine Rolle, weil Stroustrup (ich nehme an, mit Unterstützung anderer C++-Architekten) wollte, dass die Überladungsauflösung "kontextunabhängig" ist. Siehe 7.4.1 - "Overloading and Return Type" aus der "C++ Programming Language, Third Edition".

Der Grund dafür ist, dass die Auflösung für einen einzelnen Operator oder Funktionsaufruf kontextunabhängig bleiben soll.

Sie wollten, dass es nur darauf ankommt, wie die Überladung aufgerufen wurde - und nicht, wie das Ergebnis verwendet wurde (falls es überhaupt verwendet wurde). In der Tat werden viele Funktionen ohne Verwendung des Ergebnisses aufgerufen oder das Ergebnis wird als Teil eines größeren Ausdrucks verwendet. Ein Faktor, der bei dieser Entscheidung sicher eine Rolle gespielt hat, war, dass, wenn der Rückgabetyp Teil der Auflösung wäre, es viele Aufrufe überladener Funktionen gäbe, die mit komplexen Regeln aufgelöst werden müssten oder bei denen der Compiler eine Fehlermeldung ausgeben müsste, dass der Aufruf mehrdeutig ist.

Und weiß Gott, die Auflösung von Überlastungen in C++ ist auch so schon komplex genug...

5voto

Cheery Punkte 22879

In Haskell ist das möglich, obwohl es keine Funktionsüberladung gibt. Haskell verwendet Typklassen. In einem Programm könnte man sehen:

class Example a where
    example :: Integer -> a

instance Example Integer where  -- example is now implemented for Integer
    example :: Integer -> Integer
    example i = i * 10

Die Funktionsüberladung selbst ist nicht so beliebt. Die meisten Sprachen, in denen ich es gesehen habe, sind C++, vielleicht Java und/oder C#. In allen dynamischen Sprachen ist es eine Kurzform für:

define example:i
  i type route:
    Integer = [i & 0xff]
    String = [i upper]

def example(i):
    if isinstance(i, int):
        return i & 0xff
    elif isinstance(i, str):
        return i.upper()

Deshalb hat es keinen großen Sinn. Die meisten Menschen interessieren sich nicht dafür, ob die Sprache ihnen helfen kann, eine einzige Zeile zu schreiben, wo auch immer sie sie verwenden.

Der Musterabgleich ähnelt in gewisser Weise dem Überladen von Funktionen, und ich vermute, dass er manchmal ähnlich funktioniert. Es ist jedoch nicht üblich, da es nur für wenige Programme nützlich ist und in den meisten Sprachen schwierig zu implementieren ist.

Sie sehen, es gibt unendlich viele andere, besser und einfacher zu implementierende Funktionen, die in die Sprache integriert werden können, darunter:

  • Dynamische Schreibweise
  • Interne Unterstützung für Listen, Wörterbücher und Unicode-Strings
  • Optimierungen (JIT, Typ-Inferenzierung, Kompilierung)
  • Integrierte Einsatzwerkzeuge
  • Unterstützung der Bibliothek
  • Gemeinschaftliche Unterstützung und Begegnungsstätten
  • Umfangreiche Standardbibliotheken
  • Gute Syntax
  • Eval-Druckschleife lesen
  • Unterstützung für reflexive Programmierung

3 Stimmen

Haskell hat Überladung. Typklassen sind das Sprachmerkmal, das zur Definition überladener Funktionen verwendet wird.

2voto

Adam McKee Punkte 204

Gute Antworten! Insbesondere die Antwort von A.Rex ist sehr detailliert und lehrreich. Wie er hervorhebt, ist C++ fait beim Kompilieren vom Benutzer bereitgestellte Typkonvertierungsoperatoren berücksichtigen lhs = func(); (wobei func in Wirklichkeit der Name einer Struktur ist) . Meine Lösung ist etwas anders - nicht besser, nur anders (obwohl sie auf der gleichen Grundidee beruht).

Ich hingegen hatte gesucht zu schreiben...

template <typename T> inline T func() { abort(); return T(); }

template <> inline int func()
{ <<special code for int>> }

template <> inline double func()
{ <<special code for double>> }

.. etc, then ..

int x = func(); // ambiguous!
int x = func<int>(); // *also* ambiguous!?  you're just being difficult, g++!

Ich endete mit einer Lösung, die eine parametrisierte Struktur verwendet (mit T = der Rückgabetyp):

template <typename T>
struct func
{
    operator T()
    { abort(); return T(); } 
};

// explicit specializations for supported types
// (any code that includes this header can add more!)

template <> inline
func<int>::operator int()
{ <<special code for int>> }

template <> inline
func<double>::operator double()
{ <<special code for double>> }

.. etc, then ..

int x = func<int>(); // this is OK!
double d = func<double>(); // also OK :)

Ein Vorteil dieser Lösung ist, dass jeder Code, der diese Vorlagendefinitionen enthält, weitere Spezialisierungen für weitere Typen hinzufügen kann. Außerdem können Sie je nach Bedarf partielle Spezialisierungen der Struktur vornehmen. Zum Beispiel, wenn Sie eine spezielle Behandlung für Zeigertypen wünschen:

template <typename T>
struct func<T*>
{
    operator T*()
    { <<special handling for T*>> } 
};

Negativ ist, dass Sie nicht schreiben können int x = func(); mit meiner Lösung. Sie müssen schreiben int x = func<int>(); . Sie müssen explizit sagen, was der Rückgabetyp ist, anstatt den Compiler zu bitten, es durch Typumwandlungsoperatoren herauszufinden. Ich würde sagen, dass "meine" Lösung und die von A.Rex beide in eine pareto-optimale Front von Möglichkeiten, dieses C++-Dilemma zu bewältigen :)

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X