2016-12-21 14 views
2

duoというクラスを作成しようとしています。テンプレートメンバーメソッドと同じ名前の通常のメンバーメソッド

duoTオブジェクトの「テキスト」バージョンである任意のタイプTの目的ならびにstd::stringを格納するクラスです。

Tint861は、この文字列のバージョンが"861"であろうと言う。あった場合など)アイデアは、表示文字列とも価値を有することです。私はVALUEを2つのduoの間の比較に使用する予定です。NAMEは、必要なときに表示文字列として使用されます。

std::stringまたはTから構成できる必要があります。私は、その目的は、彼らが呼び出さず、後で使用できるように、両方を変換して格納することです

duo<int> anumber(861); 

をしたかのように他の言葉では、私は

std::string num("861"); 
duo<int> anumber(num); 

言うならば、同じオブジェクトを作成しています再び変換関数。私は "値"と "名前"の両方にアクセスできる必要があります。

duoは(std::stringを通る)「通常バージョン」を使用して構築されている場合、VALUEnameから変換されます。 "テンプレートバージョン"(タイプTのオブジェクトを渡す)を使用して作成された場合、NAMEvalueに変換されます。

template< class T > 
class duo 
{ 
    private: 

     T VALUE; 

     std::string NAME; 

    public: 

     // Templated version 
     duo(const T& value); 

     // Normal version 
     duo(const std::string& name); 
}; 

Tstd::stringことができる方法はありますか?これをどうやってやりますか?ありがとうございました。

+0

あなたの要件を例で説明できますか? – shafeeq

+0

なぜ両方のコンストラクタを作成しないのですか? 'duo(const std :: string&name、const T&value);' – Rene

+0

私はさらに明確にするために私の質問を編集しました@Rene –

答えて

2

:あなたはコンストラクタを専門にしたい場合はhttp://en.cppreference.com/w/cpp/language/template_specialization

EDIT

:ここ

は、リンクです私があなたの質問を編集した後に私が信じているのは、それぞれの実装がSFINAEで動作するパラメータタイプを決めることができます。

duo(const T& value); 
duo(const std::string& name); 

我々は今、時代に同じシグネチャを持っているので、これはTがstd::stringである場合には失敗します。問題はOPの質問に与えられたとして、我々は次のようにコンストラクタの2つのバージョンを書いた場合は何

テンプレートがT = std :: stringにインスタンシエートするので、シグネチャの2倍の時間が得られます。duo(const std::string& name);

SFINAEは、テンプレートを初期化できないため、インスタンス化で「不可視」です。したがって、達成したいこと:

duo(const T& value); 

このシグネチャは、コンストラクタのパラメータがクラスが作成されたものと同じである場合には常に取られます。したがって、Tがintで、入力パラメータがintの場合、このバージョンを使用する必要があります。これはで達成することができます:

template <typename X, typename std::enable_if< std::is_same<T,X>::value, 
    int>::type* = nullptr > 
duo(const X& value): VALUE(value); 

しかし、我々は、入力としてstd::stringを持っている場合、我々は、変換コンストラクタを使用します。 しかし、Tがstd::stringである特別なケースがあり、変換の必要がないため、このコンバージョンコンストラクタを使用したくないことに注意してください。だから我々はまた、上記のバージョンが欲しい。だからSFINAEを使ってそのバージョンを作るのは、Tがstd::stringではない場合にのみ有効です!試験した全ての例で

template <typename X=T, typename 
    std::enable_if<!std::is_same<X,std::string>::value, int>::type* 
     = nullptr > 
duo(const std::string& name); 

完全な作業例:

template< class T > 
class duo 
{ 
    private: 
     T VALUE; 
     std::string NAME; 

    public: 
     // Templated version 
     template <typename X, typename std::enable_if< std::is_same<T,X>::value, int>::type* = nullptr > 
     duo(const X& value): VALUE(value) 
     { 
      std::cout << "Templated version" << std::endl; 
      std::cout << __PRETTY_FUNCTION__ << value << std::endl; 

     } 

     // Normal version 
     // should NOT work if type is std::string, because in this case version above is simplier! 
     template <typename X=T, typename std::enable_if<!std::is_same<X,std::string>::value, int>::type* = nullptr > 
     duo(const std::string& name) 
     { 
      std::cout << "Normal version" << std::endl; 
      std::cout << __PRETTY_FUNCTION__ << name << std::endl; 

      std::istringstream is(name); 
      is >> VALUE; 
     } 

     T GetVal() 
     { 
      return VALUE; 
     } 
}; 

int main() 
{ 
    duo<int> int1(1); 
    duo<int> int2("123"); 

    duo<double> double1(1.234); 
    duo<double> double2("9.876"); 

    duo<std::string> string1(std::string("Hallo")); 

    std::cout << int1.GetVal() << std::endl; 
    std::cout << int2.GetVal() << std::endl; 
    std::cout << double1.GetVal() << std::endl; 
    std::cout << double2.GetVal() << std::endl; 

    std::cout << string1.GetVal() << std::endl; 
} 

SFINAEは11が、std::enable_ifstd::is_sameは、あなたの自己によって書かれた、あるいは単にSTLからコピーする必要があり、C++なしでも動作します。

テンプレートの任意の種類のインスタンスを使用する場合は、テンプレートが記述する「もの」のインスタンスを取得:あなたのコメントに答えるために

。テンプレートクラスの新しいテンプレートインスタンスは、そのインスタンスの新しいコードを生成します。テンプレートコンストラクタでも同じです。しかし、オーバーヘッドは手作業でコードを書くのと同じです。 SFINAEはオーバーヘッドを発生させません!すべてのアクションはコンパイル時に実行されます。

+0

これは面白いです。これは追加のオーバーヘッドを追加しますか?私はクラスをシンプルで読みやすいものにしようとしています。 –

+0

@DustinGoodson:あなたのコメントに関して私の答えにいくつかの情報を追加しました。また、あなたのニーズに合ったフルバージョンのサンプルを追加しました。 – Klaus

+0

ありがとうKlaus、あなたの 'main()'はすばらしく見えます、それはまさに私がこのクラスを呼び出せるようにしたいのです。このSFINAEの詳細については、 'std :: enable_if'と' std :: is_same'を学んでください。これらは私にとって新しい概念です。私が理解すると、私はあなたを答えとして受け入れます。 –

1

これはうまくいきません。場合

duo(const std::string& name, bool /* anonymous, not really used */); 

:あなたはこの区別をしたい場合は、一つの解決策は、非テンプレートバージョンにタグパラメータを追加することです

duo.cpp:22:20: required from here 
duo.cpp:18:9: error: ‘duo<T>::duo(const string&) [with T = std::__cxx11::basic_string<char>; std::__cxx11::string = std::__cxx11::basic_string<char>]’ cannot be overloaded 
duo.cpp:15:9: error: with ‘duo<T>::duo(const T&) [with T = std::__cxx11::basic_string<char>]’ 

Tstd::stringある場合は、同じシグネチャの2つのコンストラクタを定義していますそれは特殊なケースと考えられ、 "通常の"コンストラクタを使用する以外のプロパティを初期化していることを明確にしたい場合は、コンストラクタをプライベートにしてファクトリメソッドを使用して代わりに呼び出すこともできます:

class duo { 
    // ... 
    public: 
     static duo fromName(const std::string& name) { 
      return {name, true}; 
     } 
    private: 
     duo(const std::string& name, bool); 
}; 

もちろん、どちらか一方でも、両方のコンストラクタでも同じことができます。 1つのパラメータでクラスを構築するだけで明確な結果が得られない場合は、明確なセマンティクスを持つことは常に良い考えです。

+0

答えをありがとう。はい、それは私が得ていたのと同じエラーだった...私は第二のパラメータを必要とするコンストラクタを持っているとは思わない。 –

+0

ファクトリメソッドはどうですか?'duo a =デュオ :: fromName(name);デュオ b =デュオ :: fromValue(値) ' –

4

テンプレートの特殊化が必要です。 intstd::stringに暗黙的に変換できないため

template< class T > 
class duo 
{ 
    private:  
     T VALUE; 
     std::string NAME; 
    public: 
     // Templated version 
     duo(const T& value) : NAME(value), VALUE(value) // please make sure that NAME(value) works! 
     { 
      std::cout<<"template version"; 
     } 
}; 


template<> 
class duo<std::string> 
{ 
    private: 
     std::string VALUE; 
     std::string NAME; 
    public: 
     // Normal version 
     duo(const std::string& name): NAME(name), VALUE(name) 
     { 
      std::cout<<"normal version"; 
     } 
}; 

int main() 
{ 
    duo<std::string> d(std::string("abc")); // normal version 
    // duo<int> d2(1); // templated version 


    return 0; 
} 

duo<int> d2(1)
は動作しません:以下のコードを参照してください。これはあなたが次にすべきことです。あなたのコンストラクタのバージョンだけが1つのクラス定義の両方が必要な場合

template< class T > 
class duo 
{ 
    private: 
     T VALUE; 
     std::string NAME; 
    public: 
     // Templated version 
     template<class V> 
     duo(const V& value) 
     { 
      std::cout<<"template version"; 
     } 
     // Normal version 
     duo(const std::string &name) 
     { 
      std::cout<<"normal version"; 
     } 
}; 
+0

私は誤解されているようです。私は、 'T = std :: string'を特殊なケースとして扱わずに、' T'に両方の工法を利用できるようにすることを目標と考えました。 OPは何を言いますか? –

+0

@TheVee私の理解として、OPには2つのケースが必要だと思います.std :: string以外のすべてのタイプのテンプレートケース、 'std :: string'のもう1つのケースです。 – Yves

+0

コンストラクタ内には見ることのできない変換関数があります。基本的には、 'std :: string'または' T'のいずれかと、変換関数を使用して作成したもののどちらかをとります。私は 'T'が真の値を必要とする比較や何のためにも使われることを望み、' std :: string'は単に表示文字列になります。言い換えれば、 'duo 'を作成した場合、 '4321'または' std :: string( "4321") 'のどちらかを渡すことができ、どちらもまったく同じオブジェクトを作成します。変換関数は、基本的に 'std :: stringstream'を使ってこのすべてを行います。 –

関連する問題