web-dev-qa-db-ja.com

クラステンプレートでの演算子のオーバーロード

テンプレートクラスの演算子のオーバーロードを定義するときに問題が発生しました。たとえば、この架空のクラスを見てみましょう。

template <class T>
class MyClass {
  // ...
};
  • 演算子+ =

    // In MyClass.h
    MyClass<T>& operator+=(const MyClass<T>& classObj);
    
    
    // In MyClass.cpp
    template <class T>
    MyClass<T>& MyClass<T>::operator+=(const MyClass<T>& classObj) {
      // ...
      return *this;
    }
    

    このコンパイラエラーの結果:

    no match for 'operator+=' in 'classObj2 += classObj1'
    
  • 演算子<<

    // In MyClass.h
    friend std::ostream& operator<<(std::ostream& out, const MyClass<T>& classObj);
    
    
    // In MyClass.cpp
    template <class T>
    std::ostream& operator<<(std::ostream& out, const MyClass<T>& classObj) {
        // ...
        return out;
    }
    

    このコンパイラ警告の結果:

    friend declaration 'std::ostream& operator<<(std::ostream&, const MyClass<T>&)' declares a non-template function
    

ここで何が悪いのですか?

16
Pieter
// In MyClass.h
MyClass<T>& operator+=(const MyClass<T>& classObj);


// In MyClass.cpp
template <class T>
MyClass<T>& MyClass<T>::operator+=(const MyClass<T>& classObj) {
  // ...
  return *this;
}

これはテンプレートには無効です。演算子の完全なソースコードは、それが使用されるすべての変換単位に含まれている必要があります。これは、通常、コードがヘッダーにインラインであることを意味します。

編集:技術的には、標準によれば、テンプレートをエクスポートすることは可能ですが、それをサポートするコンパイラはほとんどありません。さらに、テンプレートがMyClass.cppでTであるすべての型に対して明示的にインスタンス化されているが、実際には通常、テンプレートの要点を覆す場合も、上記を実行できます。

さらに編集:私はあなたのコードを読み通しましたが、たとえば、operator []のオーバーロードなど、いくつかの作業が必要です。さらに、通常、テンプレートパラメータの一部をディメンションにして、コンパイル時に+または+ =の失敗をキャッチし、型をスタックに割り当てることができます。例外クラスもstd :: exceptionから派生する必要があります。ただし、これらはいずれもコンパイル時エラーを伴うものではなく、優れたコードではありません。

10
Puppy

あなたは次のように言う必要があります(あなたはそれを単に専門化するのではなく、全体としてテンプレートを友好的にするので、その場合は<>operator<<の後に追加する必要があるだけです。 ):

template<typename T>
friend std::ostream& operator<<(std::ostream& out, const MyClass<T>& classObj);

実際には、プライベートまたは保護されたメンバーにアクセスしない限り、フレンドとして宣言する必要はありません。 warningを取得するだけなので、友情の宣言は良い考えではないようです。 single specializationをフレンドとして宣言したい場合は、以下のように、クラスの前にテンプレートの前方宣言を行うことで、operator<<が認識されます。テンプレートとして。

// before class definition ...
template <class T>
class MyClass;

// note that this "T" is unrelated to the T of MyClass !
template<typename T>
std::ostream& operator<<(std::ostream& out, const MyClass<T>& classObj);

// in class definition ...
friend std::ostream& operator<< <>(std::ostream& out, const MyClass<T>& classObj);

上記とこの方法の両方で、その特殊化をフレンドとして宣言しますが、最初のものはall特殊化をフレンドとして宣言し、2番目はoperator<<の特殊化をTは、友情を与えるクラスのTと同じです。

他の場合、宣言は問題ないように見えますが、TUが異なる場合、+=MyClass<T>MyClass<U>することはできません。その宣言を持つ型(これらの型間で暗黙の変換がない場合を除く)。 +=をメンバーテンプレートにできます

// In MyClass.h
template<typename U>
MyClass<T>& operator+=(const MyClass<U>& classObj);


// In MyClass.cpp
template <class T> template<typename U>
MyClass<T>& MyClass<T>::operator+=(const MyClass<U>& classObj) {
  // ...
  return *this;
}

http://www.parashift.com/c++-faq-lite/template-friends.html

これはまったく同じ問題で私を助けました。

Soln:

  1. クラス自体の定義の前に、フレンド関数を前方宣言します。例:

       template<typename T> class MyClass;  // pre-declare the template class itself
       template<typename T> std::ostream& operator<< (std::ostream& o, const MyClass <T>& x);
    
  2. 関数名に「<>」を追加して、クラスでフレンド関数を宣言します。

       friend std::ostream& operator<< <> (std::ostream& o, const Foo<T>& x);
    
5
user487478

フレンドがテンプレート関数であることを指定する必要があります。

MyClass<T>& operator+=<>(const MyClass<T>& classObj);

詳細は this C++ FAQ Lite answerを参照してください。

0
wilhelmtell