2013-06-07 10 views
7

C++(11)<type_traits>の使い方を理解しようとしています。私は、GCCは、テンプレートパラメータUを推測できない理由見当もつかないテンプレート引数の種類C++での除算が失敗する11 <type_traits>

/home/per/f.cpp: In function ‘int main(int, const char**, const char**)’: 
/home/per/f.cpp:15:23: error: no matching function for call to ‘add(unsigned int&, int&)’ 
    auto a = add(ui, i); 
        ^
/home/per/f.cpp:15:23: note: candidate is: 
/home/per/f.cpp:5:10: note: template<class U, class S> U add(typename std::enable_if<std::is_unsigned<U>::value, U>::type, typename std::enable_if<std::is_signed<S>::value, S>::type) 
inline U add(typename std::enable_if<std::is_unsigned<U>::value,U>::type a, 
     ^
/home/per/f.cpp:5:10: note: template argument deduction/substitution failed: 
/home/per/f.cpp:15:23: note: couldn't deduce template parameter ‘U’ 
    auto a = add(ui, i); 
        ^

としてGCC 4.8.1もエラーでコンパイルしたとき

は、ここに私の些細なテストプログラム

#include <type_traits> 

template<class U, class S> 
inline U add(typename std::enable_if<std::is_unsigned<U>::value,U>::type a, 
      typename std::enable_if<std::is_signed <S>::value,S>::type b) 
{ 
    return a + b; 
} 

int main(int argc, const char * argv[], const char * envp[]) 
{ 
    unsigned int ui; 
    int i; 
    auto a = add(ui, i); 
    return 0; 
} 

です。誰も私のコードが欠けている情報を知っています。つまり、C++ 11で、符号なし整数型を第1引数として取り込み、整数型を第2引数として署名したプログラムを記述する方法はありますか?

+0

あなたは '予選の左タイプをdecuceすることはできません::'。 –

+0

** 14.8.2.5タイプ[temp.deduct.type]からのテンプレート引数の控除** 5推定されていないコンテキストは次のとおりです。 - 修飾IDを使用して指定されたタイプのネストされた名前の指定子。 – TemplateRex

+0

すてきなコメントと回答ありがとう! –

答えて

13

typename std::enable_if<std::is_unsigned<U>::value,U>::typeは、推測可能な文脈ではありません。これからUを推測するには、コンパイラはstd::enable_ifの逆の操作を適用する必要があります。あまりにも難しくはありませんが、これは本当ですが、それはあなたがenable_ifのような簡単なことを話しているからです。これをすべての特性の中で要求することは不可能なので、C++は涼しく、奇妙なルールの例外を作らない。一般的には推論的ではないが、これでは推論できない。

あなたがこの方法の代わりにそれを行うことができます。

template<class U, class S, 
     EnableIf<std::is_unsigned<U>, std::is_signed<S>>...> 
     // see http://flamingdangerzone.com/cxx11/2012/06/01/almost-static-if.html 
U add(U a, S b) 

それとも、単に余分な不履行の引数を追加することができ、適切にそのスタイルをサポートしていないコンパイラで:

template<class U, class S> 
U add(U a, S b, 
     typename std::enable_if<std::is_unsigned<U>::value 
      && std::is_signed<S>::value,void>::type* = nullptr) 

を...か戻り値のタイプを混乱させる。

template<class U, class S> 
typename std::enable_if<std::is_unsigned<U>::value 
    && std::is_signed<S>::value,U>::type 
add(U a, S b) 
+0

うわー...これは何をしていますか? 'typename std :: enable_if ::値 && std :: is_signed :: value、void> :: type * = nullptr'です。 rvalueからポインタへ? –

2

"入れ子になったtypedef"式からテンプレートパラメータを推論することはできません。つまり、Usome_template<U>から推測することはできますが、some_template<U>::typeから推測することはできません。

コンパイラは、some_templateのすべての(無限!)インスタンスを列挙することはできません。また、ネストされたtypedefが実際の引数の型と等しいかどうかを確認することはできません。

3

より先にの型を推論する必要があります。

それは次のようになります。あなたは、コンパイラにUSを推測するチャンスを与えていない

template <typename U, typename S> 
typename std::enable_if<std::is_unsigned<U>::value && 
         std::is_signed<S>::value>, U>::type 
add(U u, S s) 
{ 
    // ... 
} 
7

。あなたは次のようにあなたの関数を書き換え、およびテンプレートのパラメータリストにSFINAEチェックを移動することができます。

template<class U, class S, 
    typename std::enable_if<std::is_unsigned<U>::value && 
          std::is_signed <S>::value 
     >::type* = nullptr> 
inline U add(U a, S b) 
{ 
    return a + b; 
} 

ここlive exampleです。

2

試してみてください。

template<class U, class S> 
typename std::enable_if<std::is_unsigned<U>::value && std::is_signed<S>,U>::type 
add(U a , S b) 
{ 
    return a + b; 
} 
+0

個人的には、関数の宣言がはっきりしているので、私は戻り値の型でenable_ifを書くことを好みます。さらに、(あなたの場合のように)テンプレートの控除作業を行います。 – Manu343726

+0

** inline **キーワードの使用は避けてください。 C++コンパイラは、より効率的だと思えば、すべての関数をインライン化します。あなたが**インラインで**怒っている場合でも**。そして、**インライン**を入れても、インラインでその関数が効率的ではないとコンパイラが判断した場合、関数をインライン化しません。だから、**インライン**を書かないでください。 ***あなたはコンパイラ***よりも最適化されていません。シンプルで明確なコードを書いて、コンパイラに任せましょう。 – Manu343726

関連する問題