Wir alle wissen, dass Sie eine Funktion gemäß den Parametern überladen können:
int mul(int i, int j) { return i*j; }
std::string mul(char c, int n) { return std::string(n, c); }
Kann man eine Funktion entsprechend dem Rückgabewert überladen? Definieren Sie eine Funktion, die je nach Verwendung des Rückgabewerts unterschiedliche Dinge zurückgibt:
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)
Sie können davon ausgehen, dass der erste Parameter zwischen 0 und 9 liegt, Sie müssen die Eingabe nicht überprüfen oder eine Fehlerbehandlung durchführen.
Sie müssen dem Compiler mitteilen, welche Version verwendet werden soll. In C++ können Sie dies auf drei Arten tun.
Unterscheiden Sie die Aufrufe explizit durch Typisierung
Sie haben etwas geschummelt, weil Sie eine Ganzzahl an eine Funktion gesendet haben, die auf ein Zeichen wartet, und fälschlicherweise die Zahl sechs gesendet haben, wenn der Zeichenwert von ‘6’ nicht 6, sondern 54 (in ASCII) ist:
std::string mul(char c, int n) { return std::string(n, c); }
std::string s = mul(6, 3); // s = "666"
Die richtige Lösung wäre natürlich
std::string s = mul(static_cast<char>(54), 3); // s = "666"
Das war erwähnenswert, denke ich, auch wenn Sie die Lösung nicht wollten.
Unterscheiden Sie die Aufrufe explizit durch Dummy-Zeiger
Sie können jeder Funktion einen Dummy-Parameter hinzufügen, wodurch der Compiler gezwungen wird, die richtigen Funktionen auszuwählen. Am einfachsten ist es, einen NULL-Dummy-Zeiger des gewünschten Typs für die Rückgabe zu senden:
int mul(int *, int i, int j) { return i*j; }
std::string mul(std::string *, char c, int n) { return std::string(n, c); }
Was mit dem Code verwendet werden kann:
int n = mul((int *) NULL, 6, 3); // n = 18
std::string s = mul((std::string *) NULL, 54, 3); // s = "666"
Unterscheiden Sie die Aufrufe explizit, indem Sie den Rückgabewert mit Vorlagen versehen
Mit dieser Lösung erstellen wir eine „Dummy“-Funktion mit Code, der nicht kompiliert wird, wenn er instanziiert wird:
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 ;
}
Sie werden feststellen, dass diese Funktion nicht kompiliert wird, was gut ist, da wir nur einige eingeschränkte Funktionen durch die Vorlagenspezialisierung verwenden möchten:
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)) ;
}
Daher wird der folgende Code kompiliert:
int n = mul<int>(6, 3); // n = 18
std::string s = mul<std::string>(54, 3); // s = "666"
Aber dieses wird nicht:
short n2 = mul<short>(6, 3); // error: assignment of read-only variable ‘k’
Die Aufrufe explizit differenzieren, indem der Rückgabewert mit Templates versehen wird, 2
Hey, du hast auch geschummelt!
Richtig, ich habe die gleichen Parameter für die beiden “überladenen” Funktionen verwendet. Aber Sie haben mit dem Schummeln angefangen (siehe oben) …
^_^
Im Ernst, wenn Sie andere Parameter benötigen, müssen Sie mehr Code schreiben und dann beim Aufrufen der Funktionen explizit die richtigen Typen verwenden, um Mehrdeutigkeiten zu vermeiden:
// 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) ;
}
Und dieser Code würde als solcher verwendet werden:
int n = mul<int>(6, 3); // n = 18
std::string s = mul<std::string>('6', 3); // s = "666"
Und die folgende Zeile:
short n2 = mul<short>(6, 3); // n = 18
Würde immer noch nicht kompilieren.
Fazit
Ich liebe C++…
😛
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;
};
Nicht, dass ich das verwenden würde.
Wenn du machen wolltest mul
eine echte Funktion anstelle einer Klasse sein, könnten Sie einfach eine Zwischenklasse verwenden:
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);
}
Auf diese Weise können Sie Dinge wie das Passen tun mul
als Funktion in Standardalgorithmen:
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;
}
Nein.
Sie können nicht mit dem Rückgabewert überladen, da der Aufrufer alles (oder nichts) damit machen kann. Erwägen:
mul(1, 2);
Der Rückgabewert wird einfach verworfen, sodass es keine Möglichkeit gibt, eine Überladung allein auf der Grundlage des Rückgabewerts auszuwählen.
Verwenden Sie die implizite Konvertierung in einer Zwischenklasse.
class BadIdea
{
public:
operator string() { return "silly"; }
operator int() { return 15; }
};
BadIdea mul(int, int)
Du verstehst die Idee, aber eine schreckliche Idee.
Sei mul eine Klasse, mul(x, y) ihr Konstruktor und überlade einige Casting-Operatoren.
Sie können eine Funktion nicht nur basierend auf dem Rückgabewert überladen.
Obwohl dies streng genommen keine überladene Funktion ist, könnten Sie als Ergebnis Ihrer Funktion eine Instanz einer Klasse zurückgeben, die die Konvertierungsoperatoren überlädt.
Hmm, folgendes Code-Projekt-Artikel scheint zu tun, was Sie wollen. Muss magisch sein 😉
– SmacL
22. Oktober 2008 um 15:13 Uhr
Ich wünschte, es wäre möglich. Ich bin auf ein paar Fälle gestoßen, in denen ich es hätte verwenden können, um den Code zu vereinfachen …
– Brian Knoblauch
22. Oktober 2008 um 15:15 Uhr
@Brian – der Effekt ist möglich. Siehe die Antworten unten.
– Aaron
22. Oktober 2008 um 17:21 Uhr
Aber nur weil du es kannst, heißt das nicht, dass du es solltest. 🙂 Schön, jemandem das Leben schwer zu machen…
– Gishu
23. Oktober 2008 um 8:25 Uhr
Hier findet nur eine implizite Konvertierung statt, keine Überladung
– jaraaj
23. Oktober 2008 um 8:46 Uhr