C++で関数の戻り値の型のオーバーロードをモックする方法を見つける必要があります。
それを直接行う方法がないことは知っていますが、すぐに使える方法がいくつかあることを願っています。ユーザーが作業するためのAPIを作成しています。ユーザーは、文字列情報に基づいて値を取得するデータ文字列を渡します。これらの値は異なるタイプです。本質的に、私たちは彼らにそうさせたいと思います:
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
しかし、それは(明らかに)C++では機能しません。現在、次のように設定されています。
int = RetrieveValueInt(dataString1);
double = RetrieveValueDouble(dataString2);
ただし、データ文字列のタイプを知る必要はありません。
残念ながら、外部ライブラリの使用は許可されていないため、Boostは使用できません。
これを回避する方法はありますか?
明確にするために、私はC++がネイティブにそれを行うことができないことを理解しています。しかし、それを回避する方法がいくつかあるに違いありません。たとえば、RetrieveValue(dataString1、GetType(dataString1))を実行することを考えました。 GetTypeも戻り値の型を1つしか持てないため、実際には何も修正されません。しかし、私はそのようなものが必要です。
この質問は以前に尋ねられたことを理解していますが、別の意味で。明白な答えはどれも使用できません。それが私にとって有用であるためには、完全に標準のものが必要ですが、これは、他の質問の回答のいずれにも当てはまりませんでした。
あなたはこれから始めなければなりません:
template<typename T>
T RetrieveValue(std::string key)
{
//get value and convert into T and return it
}
この関数をサポートするには、値をT
型に変換するために、もう少し作業する必要があります。値を変換する簡単な方法の1つは次のとおりです。
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");
}
この関数を次のように呼び出す必要があることに注意してください。
int x = RetrieveValue<int>(key);
代わりにこれを行うことができれば、int
を2回言及することを回避できます。
Value RetrieveValue(std::string key)
{
//get value
std::string value = get_value(key, etc);
return { value };
}
ここで、Value
は次のように実装されます。
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");
}
}
次に、これを書くことができます:
int x = RetrieveValue(key1);
double y = RetrieveValue(key2);
どちらがいいですか?
これを行う正しい方法は、戻り値をパラメーターに移動することだけです。
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);
オーバーロードであっても特殊化であっても、関数のシグネチャに情報を含める必要があります。変数を未使用の2番目の引数として渡すことができます。
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);
データ文字列がコンパイル時の定数である場合(私のコメントへの回答で述べたように)、テンプレートマジックを使用して作業を行うことができます。さらに簡単なオプションは、文字列をまったく使用せず、引数をオーバーロードできるデータ型を使用することです。
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
値がゼロや負の値になり得ないことがわかっている場合は、intとdoubleを保持する構造体を返し、不要なものをゼロにしてください...
それは安くて汚いですが、簡単な方法です...
struct MyStruct{
int myInt;
double myDouble;
};
MyStruct MyFunction(){
}
残念ながら、関数の戻り値の型をオーバーロードする方法はありません。この回答を参照してください 戻り値の型によるオーバーロード
int a=itoa(retrieveValue(dataString));
double a=ftoa(retrieveValue(dataString));
どちらも文字列を返します。
テンプレートソリューションの代わりに、関数にクラスへの参照またはポインターを返させてから、そのクラスのサブクラスを作成して、返したいさまざまなデータ型を含めることができます。 RetrieveValue
は、適切なサブクラスへの参照を返します。
これにより、ユーザーは、どのサブクラスに属しているかを知らなくても、返されたオブジェクトを他の関数に渡すことができます。
この場合の問題は、メモリ管理の1つになります-返されたオブジェクトを割り当てる関数とそれを削除する関数を選択し、いつ、メモリリークを回避するかを選択します。