2009-06-18 7 views
14

私はそれが割り当てられている変数の型に基づいて関数の戻り値の型を変更することが可能かどうか疑問に思っていました。ここに、私が意味するものの簡単な例があります。テンプレートの特殊化なしで関数の戻り値の型を変更しています。 C++

文字列からint、bool、またはfloatの変数を解析する関数を作成します。私は、この関数テンプレートを作成する場合たとえば...

Int value = parse("37"); 
Float value = parse("3.14"); 
Bool value = parse("true"); 

私は変数の型は常に文字列であることを行っている引数リストから決定されなければならないことを、理解しています。 C++でこれを行う他の方法はありますか?

答えて

31

。ちなみに、クラスを直接使うこともできます。私はそれだけ出来事への変換プロキシだが、それ自体は変換私は自分自身よりも少し速かったlitbに同意するだろう

struct conversion_proxy { 
    string str; 
    conversion_proxy(string const &str):str(str) { } 
    template<typename T> operator T() { 
     return boost::lexical_cast<T>(str); 
    } 
}; 

float a = conversion_proxy("3.1"); 
+0

これはちょっと間違っています。あなたは()演算子を呼び出すのではなく、単に演算子を呼び出すことになります。私は確信していないので、私はダウン投票していない。どうか明らかにしてください。 –

+0

i)は(OPを呼び出すつもりはなかった:)私はプロキシを返された関数を呼び出しました。 2番目の部分では、間に介在する関数なしでクラスを直接使用することを提案しています –

+0

ありがとうGMan、ありがとう:) –

0

残念ながら、これは不可能です。 C++では、戻り値に基づいて関数をオーバーロードすることはできません。 ParseInt、ParseFloat、およびParseBoolの3つの関数を持つか、関数テンプレートを使用する必要があります。

+0

技術的に真(。またlive demoを参照)しますが、ユーザー定義の変換でずっと同じことを達成することができます - litbの優れた答えを参照してください。 –

0

void *を返し、必要に応じて結果をキャストできます。

私はこれに反対します。 C++は強く型付けされた言語です。これの利点は、コンパイラが動的に型指定された言語よりも早くエラーを捕捉できることです。

+0

どちらも実際には動作しません。 void *を返すコードは、void *がfloat *かint *にキャストされるかどうかは分かりません。 Boost :: variantはより安全です。なぜなら、明確に定義された問題で失敗するからです。しかし、同じクリスタルボールの問題があります。 – MSalters

12

あなたが知っているかどうかわからないのですが、実際にはテンプレートでこれを行うことができます。唯一のキャッチは、推論に頼るのではなく、各呼び出しで変換する型を指定する必要があるということです(引数の型は常に同じであると言います)。その後、

template<typename T> T parse(const string& str) { /* do stuff for other types */ } 
template<> int parse<int>(const string& str) { /* do stuff for ints */ } 
template<> double parse<double>(const string& str) { /* do stuff for doubles */ } 
template<> bool parse<bool>(const string& str) { /* do stuff for bools */ } 
// etc. 

そして、あなたはすでにこれはちょうどこの答えを無視知っていたが、それはあなたが、これが可能であることを認識していることをあなたの質問から明らかではない場合

int value = parse<int>("37"); 
double value = parse<double>("3.14"); 
bool value = parse<bool>("true"); 

として起動します。

もちろん、あなたがやっていることが本当に一般的ではない(そしてあなたが解析する各タイプに特化しなければならない)なら、テンプレートを書くことはとにかく正しいことではありません。ところで、あなたは(パースは、あなたが本当に何をしたいかであると仮定して)このような単一の機能ではかなり一般的にそれを行うことができます

#include <sstream> 
template<typename T> T parse(const string& str) 
{ 
    T t; 
    std::istringstream sstr(str); 
    sstr >> t; 
    return t; 
} 

これは、任意のデフォルト・構築可能、合理化のために動作します抽出可能なタイプ。すべての組み込み関数を含みます。

+0

これは、 "int parse_int(const string&)"、 "double parse_double(const string&)"などよりも優れています。 –

+3

これはテンプレートを使って行うことができたことを示しています。テンプレートが実際にジェネリックでない場合は、それがどのように一般的であるかを示しました(多くのタイプ)。 –

+0

+1 - これはこの問題を定義するより良い方法です。 –

0

C++ではこのタイプの動作はできません。許容されるためには、戻り値の型だけが異なる同じスコープで同じ名前の関数を定義する必要があります。これはC++では合法ではありません。

C++では、オーバーライドされた仮想関数で共変の戻り値の型など、いくつかの戻り型の特殊化を行うことができます。しかし、それはあなたが探しているものをサポートしていません。

+0

技術的にはあなたがC++での戻り値の型によってオーバーロードすることはできませんが、ユーザー定義の変換とずっと同じことを達成できること、それは本当のlexical_castブースト本質的です。 –

5

出力引数をポインタまたは参照として渡すことができます。このよう

:このような

template<class T> void parse(const std::string &input, T& output); 

次にコード:

double d; parse(input, d); 
int i; parse(input, i); 

が動作するはずです。

しかし、あなたのコードは、STDのための完璧なフィットのように見える::だけだろうistringstream:

istringstream is(input); 
input >> d; 

あなたが関与やや複雑なフォーマットを持っている場合は、私はかなり良い運を持っていたトリックを作成する必要がデータを引き出すカスタム演算子>>を持つカスタムオブジェクト。

そして、それは次のように次のようになります。今、あなたはちょうど

float a = parse("3.1"); 

を行う必要があり、それがうまく動作するはずです。これは、

struct proxy { 
    string str; 
    proxy(string const &str):str(str) { } 
    template<typename T> operator T() { 
     return boost::lexical_cast<T>(str); 
    } 
}; 

proxy parse(string const &str) { return proxy(str); } 

変換機能を使って行うことができ

istringstring is(input); 
input >> LineExtracter(x, y, d); 
3

をしないという事実を指すようにconversion_proxyにそれをリネームお勧めします。キャスト演算子を使用します。

#include <iostream> 
#include <string> 
#include <sstream> 

class Convertible 
{ 
public: 
    int m_Integer; 
    bool m_Bool; 
    double m_Double; 

    Convertible() : m_Integer(0), m_Bool(false), m_Double(0.0) {}; 

    operator int() const 
    { 
     return m_Integer; 
    } 
    operator bool() const 
    { 
     return m_Bool; 
    } 
    operator double() const 
    { 
     return m_Double; 
    } 
}; 

Convertible parse(std::string data) 
{ 
    Convertible l_result; 

    std::istringstream converter(data); 
    converter >> l_result.m_Integer; 

    std::istringstream converter2(data); 
    converter2 >> l_result.m_Bool; 

    std::istringstream converter3(data); 
    converter3 >> l_result.m_Double; 

    return l_result; 
} 

void main() 
{ 
    int l_convertedInt = parse("2"); 
    bool l_convertedBool = parse("true"); 
    double l_convertedDouble = parse("3.14"); 

    std::cout << "Converted '2' to " << l_convertedInt << std::endl; 
    std::cout << "Converted 'true' to " << l_convertedBool << std::endl; 
    std::cout << "Converted '3.14' to " << l_convertedDouble << std::endl; 
} 
0

はここparse()への引数が文字列以外の型である私の状況について@Tyler McHenry's answerの私の適応です。私は型変換警告intfloat)を避けるために、テンプレートの特殊を導入しなければならなかった見つけ

注意。

#include <iostream> 

struct MyUnion 
{ 
public: 
    union { 
    bool bool_value; 
    int int_value; 
    float float_value; 
    }; 
}; 

template<typename T> T parse(const MyUnion& h) 
{ 
    T t; 

    if (typeid(T) == typeid(bool)) { 
    t = h.bool_value; 
    } else if (typeid(T) == typeid(int)) { 
    t = h.int_value; 
    } else if (typeid(T) == typeid(float)) { 
    // t = h.float_value; // see **Warning** below; use float specialization instead 
    } 

    return t; 
} 

// 'float' template specialization to avoid conversion warning. 
template<> float parse(const MyUnion& h) 
{ 
    return h.float_value; 
} 

int main() 
{ 
    MyUnion mu1; mu1.bool_value = true; 
    MyUnion mu2; mu2.int_value = 42; 
    MyUnion mu3; mu3.float_value = 3.14159; 

    std::cout << "As bool: " << parse<bool>(mu1) << std::endl; 
    std::cout << "As int: " << parse<int>(mu2) << std::endl; 
    std::cout << "As float: " << parse<float>(mu3) << std::endl; 
} 

// **Warning** 
// In function 'T parse(const Heterogeneous&) [with T = int]': 
// Line 22: warning: converting to 'int' from 'const float' 
関連する問題