2016-12-20 1 views
7

+ =演算子が存在しないと不平を言ってコンパイルされない次のコードがあります。 + =演算子は、ここで私はクラスAの内部で演算子を実装する場合は、コードをコンパイルして動作します内部と外部のオーバーロードされたC++演算子の違い

template < typename _T > 
class A { 
public: 
    operator _T() const { return 42 ; } 
}; 

template <typename _T > 
A<_T> & operator += (A<_T> & l, _T r) { return l ; } 


int main() { 
    A<int> e, f ; 
    e += f ; 
    return 0 ; 
} 

クラスAの外で宣言されています。これらの2つのコードの違いは

template < typename _T > 
class A { 
public: 
    operator _T() const { return 42 ; } 

    A<_T> & operator += (_T r) { return *this ; } 
}; 


int main() { 
    A<int> e, f ; 
    e += f ; 
    return 0 ; 
} 

は何ですか?彼らは同等ではないと思われますか?

これはgcc 4.4.7-4でコンパイルされました。

+2

これは問題ではありませんが、アンダースコアで始まり大文字( '_T ')で始まる名前は実装で使用するために予約されています。あなたのコードでそれらを使用しないでください。 –

答えて

6

最初の例では、テンプレート引数控除は、任意の変換を行いませんので、コンパイルに失敗しました。

template <typename _T > 
A<_T> & operator += (A<_T> & l, _T r) { return l ; } 

両方lr_Tが何であるかを判断するに貢献しています。 e += fを実行すると、_Tは、lの場合はintでなければならず、rA<int>になる必要があります。そのタイプはfです。一致しないので、コンパイルに失敗します。

2番目のコードでは、テンプレート引数の引き算は行われません。コンパイラは、_Tがクラスのインスタンス化のものであることを知っているので、それができる必要があるのは、rに渡されたものを_Tに変換することだけです。


また、アンダースコアで始まる名前の習慣から抜け出すことをお勧めします。それらについてルールがあり、違反した場合、プログラムは実装のために予約されているため、未定義の動作をします。詳細はこちらWhat are the rules about using an underscore in a C++ identifier?

1

等価ではないと思われますか?

いいえ、第二の例では、既に知られているが最初の例では、_Tは、テンプレートfunction parameter which gets deducedからです。

コンパイラによって展開されている第二の例を想像:G ++ 7.0

template <> 
class A_int { 
public: 
    operator int() const { return 42 ; } 

    A<int> & operator += (int r) { return *this ; } 
}; 

は控除が失敗した理由を説明する良いのエラーメッセージを表示します。

deduced conflicting types for parameter '_T' ('int' and 'A<int>') 

可能な解決策/回避策は追加のテンプレートパラメータを追加することです:

template <typename _T, typename _U> 
A<_T> & operator += (A<_T> & l, _U r) { return l ; } 

wandbox example

0

これはややこしいことです。 2番目のケースでは、演算子はクラステンプレートAのメンバ関数であり、テンプレート自体ではありません。e += fに電話すると、の中に既に存在するA<int>::operator += (int)という一致が見つかります。 A<int>からintへの暗黙的な変換があるため、このオーバーロードは有効です。

最初のケースでは、演算子はテンプレートです。コンパイラは、コールサイトだけから引数_Tを導き出すことによってインスタンス化しようとします。テンプレート引数の控除ではユーザ定義の変換は考慮されないため、控除は失敗します。

溶液がテンプレートを介してこのような付加的な間接非推論コンテキストを利用して、控除の参加から第二パラメータを防止するためである。

template <class T> 
struct NonDeduced_ { using type = T; } 

template <class T> 
using NonDeduced = typename NonDeduced_<T>::type; 

template <typename _T > 
A<_T> & operator += (A<_T> & l, NonDeduced<_T> r) { return l ; } 

のみ最初のパラメータは、演繹に参加成功した場合、、次にの推定値_Tを使用して、2番目のパラメータに実行可能な変換があるかどうかを確認します。

3

スニークな短い答えは、ユーザコードに_Tを使用しているため、プログラム全体が正しく形成されていないため診断不要です。 _とそれに続く大文字で構成される識別子は、実装によって使用されるように予約されています。

この意味では、両方の例が完全に同等です。

エラーを無視して、それらは同一ではありません。

最初のメンバーはtemplate+=の非メンバーです。

第2は、テンプレートクラスの非テンプレートメンバー+=で、暗黙的にthisを第1パラメータとしています。

これは非常に異なるものです。 template機能パターンマッチングは変換を行いません(ベース以外)。 templateタイプのメソッドに変換できます。

第2の場合、非テンプレートoperator+=は、その第2引数をタイプ_Tに変換することができます。最初のケースでは、テンプレートoperator+=はパターンマッチングタイプ中に変換しようとしません。

私はしばしば好む第三の可能性は、実際にあります:私たちは友人として自由+=を作る

template < class T > 
struct A { 
    operator T() const { return 42 ; } 
    friend A& operator += (A& l, T r) { return l; (void)r; } 
}; 

。これにより、2つの引数をとる非テンプレート+=が作成され、より対称的になります。

このような非テンプレートの友人は、ADLで見つけることができます。

live example。いずれかのポインタが、他の同様A<_T>& (*)(A<_T>&, _T)A<_T>& (A<_T>::*)(_T)に格納することができるのでよう

はさておき、それらはまた異なります。これらは同一ではなく、間に変換することもできません。

+0

あなたは最初と2番目の逆を持っています。 – Kundor

+0

@kundがtsrifとdnocesに変更されました。どうも! – Yakk

関連する問題