2016-05-12 10 views
3

テンプレート引数に基づいて2つの異なる実装を持つテンプレート関数を作成しようとしています。最初のものはI/Oマニピュレータに固有のものであり、2つ目は一般的なものです。テンプレート型を呼び出す方法

template <typename T> 
typename std::enable_if< 
      std::is_same<decltype(std::setw), T>::value>::type 
foo(T& t){ 
     cout << "enabled" << std::endl; 
} 

template <typename T> 
void foo(T& t){ 
     cout << "Normal" << std::endl; 
} 

template <typename... Ts> 
void foohelper(Ts... t){ 
     foo(t...); 
} 
int main(){ 
     foohelper(std::setprecision(3)); // It should print enabled, but printing Normal 
} 

現在のところ、実装しようとしていることを行っていません。どうすれば修正できますか?

+1

std :: setとstd :: setprecisionは同じ型ではありません(また、std :: setprecision(3)はstd :: setprecisionと同じ型ではありません)。答え! – bendervader

+0

はい、私はstd :: setwを意味しました。それらは実際には異なっています。ソースコードはhttp://cs.brown.edu/~jwicks/libstdc++/html_user/iomanip-source.htmlを参照してください。構造体を返しますが、構造体ごとに異なる名前が付いています。 'std :: cout << typeid(decltype(std :: setw))を検証するためにこれを試してください。name()<< std :: endl; std :: cout << typeid(decltype(std :: setprecision))。name()<< std :: endl; ' – bendervader

答えて

5

enable_ifを使用する必要はありません。メイン定義のテンプレートtemplate fooに特化してください。

template <> 
void foo<decltype(std::setw(0))>(decltype(std::setw(0))& t) 
{ 
     cout << "enabled" << std::endl; 
} 
+0

ありがとうございます。私はなぜ定義をメインの下に保たなければならないのか分かりません。 – AAA

+0

これはよく見えますが、特殊化されていますが、この場合単純なオーバーロードで置き換えることはできますか? –

+0

@AAA:テンプレートの主な定義*の下、すなわちテンプレート void foo(T&t)... 'の下にある、私の部分の文法が貧弱です。テンプレートは、スペシャライゼーションの前に表示する必要があります。 –

2

これらの関数の戻り値の型は、が指定されていませんです。つまり、type_traitsを使って比較することはできません。例として、ここではlibcxxです:

// Definitions omitted 
T2 setiosflags (ios_base::fmtflags mask); 
T3 setbase(int base); 
template<charT> T4 setfill(charT c); 
T5 setprecision(int n); 
T6 setw(int n); 

T5T6などが__imo_t4など、マニピュレータの実際の作業を行い内部構造を参照してください。それらはすべて戻り値の型が異なるため、関数の型は異なります。したがって、彼らはすべて異なっているでしょう。

+2

不特定の型を使用できます。その優れた例は、 'std :: bind'の戻り型です。 –

+0

@GuillaumeRacicot' std :: bind'は、 'std :: setw'と' std :: setprecision'をどう比較するのですか? – user6323422

+0

downvotesはかなり悲しいです。 [この回答](http://stackoverflow.com/a/37177827/6323422)もコンパイルされず、[この回答](http://stackoverflow.com/a/37178008/6323422)は一般的な場合。 – user6323422

2

あなたは機能std::setprecisionの戻り値の型を比較す​​ることにより、SFINAEを使用することができます。戻り値の型は指定されていないので、decltypestd::declvalが必要です。ここで

template <typename T, typename std::enable_if<std::is_same<decltype(std::setprecision(std::declval<int>())), typename std::decay<T>::type>::value, int>::type = 0> 
void foo(T&& t) { 
    cout << "enabled" << std::endl; 
} 

が、私はそれを参照のいずれかの種類を受け入れるT&&T&を変更:

はここソリューションです。それはあなたのfoohelperの使用を取り除きます。

また、T&Tと同じではないため、タイプの一つは、参照型である場合、比較は動作しないので、私は、タイプTを減衰、型の比較です。 std::decayを使用すると、リファレンスが効果的に削除されます。

SFINAE構造体をテンプレート宣言内に移動したことに注目してください。これは、SFINAEを戻り値の型から離しておくためです。 0に既定の非型テンプレートパラメータを使用しても、関数の使用には影響はなく、誰も効果的にSFINAEチェックをバイパスできないようにします。

編集:

あなたはあなたのケースで0std::declval<int>()を置き換えることができます。私はちょうどあなたが実際の値を置き換えるためにこのツールを使用する方法を示して喜んでいた。あなたが値を持つことができない多くの場合があります。

+0

多くのありがとうございます。しかし、私はfoohelperを使う必要があります、これは私のアプリケーションが書かれている方法です。 – AAA

関連する問題