Ich muss einen Weg finden, eine Überladung eines Funktionsrückgabetyps in C++ zu verspotten.
Ich weiß, dass es keine Möglichkeit gibt, dies direkt zu tun, aber ich hoffe, es gibt ein paar Out-of-the-Box-Möglichkeiten. Wir erstellen eine API, unter der Benutzer arbeiten können, und sie übergeben eine Datenzeichenfolge, die einen Wert basierend auf den Zeichenfolgeninformationen abruft. Diese Werte sind verschiedene Typen. Im Wesentlichen möchten wir sie tun lassen:
int = RetrieveValue(dataString1);
double = RetrieveValue(dataString2);
// Obviously, since they don't know the type, they wouldn't use int =.... It would be:
AnotherFunction(RetrieveValue(dataString1)); // param of type int
AnotherFunction(RetrieveValue(dataString2)); // param of type double
Das funktioniert aber in C++ (offensichtlich) nicht. Jetzt lassen wir es so einrichten, dass sie anrufen:
int = RetrieveValueInt(dataString1);
double = RetrieveValueDouble(dataString2);
Wir möchten jedoch nicht, dass sie wissen müssen, um welchen Datentyp es sich handelt.
Leider dürfen wir keine externen Bibliotheken verwenden, also keinen Boost.
Gibt es Möglichkeiten, wie wir das umgehen können?
Zur Klarstellung verstehe ich, dass C++ das nicht nativ ausführen kann. Aber es muss einen Weg geben, um das zu umgehen. Ich dachte beispielsweise darüber nach, RetrieveValue (dataString1, GetType (dataString1)) auszuführen. Das behebt eigentlich nichts, denn GetType kann nur einen Rückgabetyp haben. Aber ich brauche so etwas.
Ich verstehe, dass diese Frage schon einmal gestellt wurde, aber in einem anderen Sinne. Ich kann keine der offensichtlichen Antworten verwenden. Ich brauche etwas völlig Neues, um es für mich nützlich zu machen, was bei keiner der Antworten in der anderen Frage der Fall war.
Sie müssen damit beginnen:
template<typename T>
T RetrieveValue(std::string key)
{
//get value and convert into T and return it
}
Um diese Funktion zu unterstützen, müssen Sie etwas mehr arbeiten, um den Wert in den Typ T
zu konvertieren. Ein einfacher Weg, um den Wert zu konvertieren, könnte folgender sein:
template<typename T>
T RetrieveValue(std::string key)
{
//get value
std::string value = get_value(key, etc);
std::stringstream ss(value);
T convertedValue;
if ( ss >> convertedValue ) return convertedValue;
else throw std::runtime_error("conversion failed");
}
Beachten Sie, dass Sie diese Funktion immer noch aufrufen müssen als:
int x = RetrieveValue<int>(key);
Sie könnten vermeiden, int
zweimal zu erwähnen, wenn Sie dies stattdessen tun könnten:
Value RetrieveValue(std::string key)
{
//get value
std::string value = get_value(key, etc);
return { value };
}
wobei Value
implementiert ist als:
struct Value
{
std::string _value;
template<typename T>
operator T() const //implicitly convert into T
{
std::stringstream ss(_value);
T convertedValue;
if ( ss >> convertedValue ) return convertedValue;
else throw std::runtime_error("conversion failed");
}
}
Dann könntest du folgendes schreiben:
int x = RetrieveValue(key1);
double y = RetrieveValue(key2);
was willst du, richtig?
Die einzige sinnvolle Möglichkeit besteht darin, den Rückgabewert zu den Parametern zu verschieben.
void retrieve_value(std::string s, double& p);
void retrieve_value(std::string s, int& p);
<...>
double x;
retrieve_value(data_string1, x);
int y;
retrieve_value(data_string2, y);
Unabhängig davon, ob es sich um eine Überladung oder um eine Spezialisierung handelt, benötigen Sie die Informationen in der Funktionssignatur. Sie können die Variable als nicht verwendetes zweites Argument übergeben:
int RetrieveValue(const std::string& s, const int&) {
return atoi(s.c_str());
}
double RetrieveValue(const std::string& s, const double&) {
return atof(s.c_str());
}
int i = RetrieveValue(dataString1, i);
double d = RetrieveValue(dataString2, d);
Wenn die Datastrings Konstanten zur Kompilierzeit sind (wie in der Beantwortung meines Kommentars erwähnt), können Sie zur Erledigung dieser Aufgabe eine Vorlagenmagie verwenden. Eine noch einfachere Option besteht darin, keine Zeichenfolgen zu verwenden, sondern einige Datentypen, die es Ihnen ermöglichen, Argumente zu überladen.
struct retrieve_int {} as_int;
struct retrieve_double {} as_double;
int RetrieveValue(retrieve_int) { return 3; }
double RetrieveValue(retrieve_double) { return 7.0; }
auto x = RetrieveValue(as_int); // x is int
auto y = RetrieveValue(as_double); // y is double
Wenn Sie wissen, dass Ihr Wert niemals etwas wie Null oder Negativ sein kann, geben Sie einfach eine Struktur mit int zurück und verdoppeln Sie diejenige, die Sie nicht brauchen ...
Es ist ein billiger und schmutziger, aber einfacher Weg ...
struct MyStruct{
int myInt;
double myDouble;
};
MyStruct MyFunction(){
}
Es gibt leider keine Möglichkeit, den Funktionstyp return zu überlasten. Siehe Antwort Überladen mit dem Typ return
int a=itoa(retrieveValue(dataString));
double a=ftoa(retrieveValue(dataString));
beide geben einen String zurück.
Als Alternative zur Vorlagenlösung können Sie die Funktion dazu veranlassen, eine Referenz oder einen Zeiger auf eine Klasse zurückzugeben, und dann Unterklassen dieser Klasse erstellen, die die verschiedenen Datentypen enthalten, die Sie zurückgeben möchten. RetrieveValue
würde dann einen Verweis auf die entsprechende Unterklasse zurückgeben.
Der Benutzer könnte dann das zurückgegebene Objekt an andere Funktionen übergeben, ohne zu wissen, zu welcher Unterklasse es gehört.
Das Problem in diesem Fall würde dann zur Speicherverwaltung werden - die Auswahl, welche Funktion das zurückgegebene Objekt zuordnet und welche Funktion es löscht, und wann, so, dass Speicherverluste vermieden werden.