web-dev-qa-db-de.com

C++ dieselben Funktionsparameter mit unterschiedlichem Rückgabetyp

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.

9
Josh Johnson

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?

22
Nawaz

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);
1
user283145

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);
1
Robᵩ

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
1
Walter

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(){
}
1
HackyStack

Es gibt leider keine Möglichkeit, den Funktionstyp return zu überlasten. Siehe Antwort Überladen mit dem Typ return

0
Twiltie
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.

0
Simon