2011-08-08 15 views
1

は私がテンプレート引数を推定できませんでしたか?

template<typename hi_t, typename lo_t> 
struct int_t 
{ 
hi_t hi; 
lo_t lo; 

int_t() : lo(0), hi(0) {} 
int_t(int value) : lo(value), hi(value<0u? -1: 0) {} 
int_t(unsigned value) : lo(value), hi(0) {} 

int_t& operator+=(const int_t& rhs) 
{ 
    lo_t _lo = lo; 

    lo += rhs.lo; 
    hi += rhs.hi; 
    hi += (int)(lo < _lo); 

    return *this; 
} 

template<typename hi_t, typename lo_t> 
inline friend int_t<hi_t, lo_t> operator+(const int_t<hi_t, lo_t>&, const int_t<hi_t, lo_t>&); 
}; 

template<typename hi_t, typename lo_t> 
int_t<hi_t, lo_t> operator+(const int_t<hi_t, lo_t>& lhs, const int_t<hi_t, lo_t>& rhs) 
{ return int_t<hi_t, lo_t>(lhs) += rhs; } 

次のコード

typedef int_t<long long, unsigned long long> int128; 

int main() 
{ 
    int128 i = 1024; 
    i = i + 20; 
} 

コンパイラを実行し、エラーを生成し、次のクラスに持っている:私はクラス内のテンプレートオペレータのコードを入れて

'int_t<hi_t,lo_t> operator +(const int_t<hi_t,lo_t> &,const int_t<hi_t,lo_t> &)' : could not deduce template argument for 'const int_t<hi_t,lo_t> &' from 'int' 

body - フレンドオペレータからテンプレートラインを削除すると動作しますが、クラス外のフレンドオペレータではオペレータを推論できません。私はコンパイラがこのテンプレート演算子のコードを生成するときに、入力パラメータと戻り値の型がint128であることを知ったので、int型からその型へのキャストに問題はありません。

UPDATE

我々は前の例を以下のように、クラス内のフレンド演算子を定義する場合は、クラスの外テンプレート演算子を定義しようとするときに問題が起こる

template<typename hi_t, typename lo_t> 
struct int_t 
{ 
hi_t hi; 
lo_t lo; 

int_t() : lo(0), hi(0) {} 
int_t(int value) : lo(value), hi(value<0u? -1: 0) {} 
int_t(unsigned value) : lo(value), hi(0) {} 

int_t& operator+=(const int_t& rhs) 
{ 
    lo_t _lo = lo; 

    lo += rhs.lo; 
    hi += rhs.hi; 
    hi += (int)(lo < _lo); 

    return *this; 
} 

friend int_t operator+(const int_t& lhs, const int_t& rhs) 
{ return int_t(lhs) += rhs; } 

}; 

を作品

+0

G ++でもコンパイルされませんが、エラーは異なります。 friend関数のテンプレートパラメータがクラスのテンプレートパラメータを陰影付けすることに戸惑う。暗黙的に20をint_tに変換することもできません。また、値<0uは常に偽です!私は私のスタイルでそれを書き直してみるでしょう、おそらく私は根本的なエラーをキャッチすることができます。 – Frigo

+0

@Frigo:友人宣言はちょっとです(ref。[C++ FAQ Lite 35.16](http://www.parashift.com/c++-faq-lite/templates.html#faq-35.16)eg)。私はMuhammadのコンパイラはそれで大丈夫だと仮定します。 –

+0

私はVC++ 2008コンパイラを使用していますが、言語拡張機能はオフになっており、レベル4の警告が表示されています。 –

答えて

4

コードは見た目よりもトリッキーです。最も難しい部分は、あなたの友人機能の宣言です。テンプレートから関数を使用することについては、answerを参照してください。短い勧告は、あなたがテンプレートoperator+を削除し、非テンプレートフレンド関数として内部クラス宣言、それを実装することである:特定のエラーのとおり

template<typename hi_t, typename lo_t> 
struct int_t 
{ 
// ... 
    friend int_t operator+(int_t lhs, const int_t& rhs) { 
     return lhs+=rhs; 
    } 
}; 

を、それがその役に立つこと、そしてそれはないかもしれませんタイプの控除後は完全一致(つまり変換は必要ありません)した場合にのみ、演算子のオーバーロードのためにテンプレートが考慮されることを考慮に入れることから始めることができます。これは、変換があっても場合int128_t + intは左側と右側の両方に同じ型を持つテンプレートoperator+によって一致することはないことを意味します。提案された解決策は、上記の宣言(および定義)

非テンプレート関数。それはクラス内で定義されているため、Argument Dependent Lookupによってのみ考慮されるため、演算子の1つがint_tである場合にのみ適用されます.ADLによって検出された場合は、通常はテンプレート以外のルールです。つまり、コンパイラはlhsまたはrhsの両方に可能な変換を使用できます(ADLによって見つかった場合はint_tインスタンシエーションでなければなりませんが、もう一方は変換されます)。

+0

うん、正しい説明、それは単純に無限の数の加算演算子にintをマッチさせません。彼は単にテンプレート宣言を完全に捨てるべきです。 int_t演算子+(const int_t&x、const int&y){...} – Frigo

+0

+1。私は私の答えを削除しました。 – Nawaz

+0

@Frigo:問題は控除を超え、 'int'と' long int'が渡されたとき(あるいは他のコンバーチブルだが異なる型のとき) 'std :: min'を考慮すると、控除の問題はなく、無限の可能性がありますが、コンパイラはそのテンプレートを使用できません。 –

-1

I'am必ず多分コンパイラが再び

をテンプレート引数を必要としません
+0

'演算子+ ='が動作し、問題は '友人演算子+'にあります –

+0

申し訳ありません読み込みが遅すぎます – Nelstaar

1

すべてのコンパイラの警告をオンにします。

あなたは良くないテンプレートクラス自体、のようにあなたのfriend宣言で同じテンプレートパラメータ名を使用しています。それらの名前を変更します。ここでは一つの解決策です:アウトオブラインオペレータの定義を削除し、このインライン定義を行います。

template<typename H, typename L> 
inline friend int_t operator+(const int_t & lhs, const int_t<H, L> & rhs) 
{ 
    return int_t(lhs) += rhs; 
} 

、あなたのRHSは任意のタイプであるため、あなたはタイプを言及する必要があります。

i = i + int128(20); 

int_t<H,L>(20)への適切な変換を実行することができるように、整数20からパラメータH,Lを推定する方法は、(ナワズの答えを参照)が存在しないからです! intからコンバージョンコンストラクタを利用するには


、あなただけの同じタイプではなく、テンプレートの他のタイプで動作することができます。このため、非テンプレートオペレータ追加:

int_t operator+(const int_t & rhs) const { return int_t(*this) += rhs; } 

を今、あなたはint_t(int)コンストラクタを使用して、i = i + 20;を言うことができます。

更新: OPは、対称呼び出し(i = 50 + i;)を可能にするために、示唆、およびのみするためにデビッドが示唆するように、我々は、単項演算子とテンプレート友人バイナリ演算子の両方を削除する必要があり、固定タイプ内の動作を可能にするように代わりにテンプレート化されていないバイナリの友人がいる:

friend int_t operator+(const int_t & lhs, const int_t & rhs) { return int_t(lhs) += rhs; } 

これは設計上の問題です。私は個人的に最終版を好むだろう。

+0

これは解決策ですが、テンプレートのすべてのインスタンス化に盲目的に賛同しているので、潜在的な専門化)。 –

+0

@David:確かに、それはOPが作るための設計上の決定です。私はおそらく、1つのタイプの操作に固執しています。あなたは空想を得たい場合は、両方の構成型が変換可能かどうかを確認するために型特性を追加することができます。 –

+0

firend演算子を次のコードに置き換えて、この例をもう一度試してみましょう:friend int_t operator +(const int_t&lhs、const int_t&rhs){return int_t(lhs)+ = rhs; } –

0

あなたはoperator+=がメンバーテンプレートであることを特定のか?通常、あなただけです

inline friend int_t operator+(const int_t&, const int_t&) {...} 
関連する問題