9

次のコード

#include <cassert>
#include <cstddef>

template <typename T>
struct foo {
    foo(std::nullptr_t) { }
    //friend bool operator ==(foo lhs, foo rhs) { return true; }

    template <typename U>
    friend bool operator ==(foo<U> lhs, foo<U> rhs);
};

template <typename T>
inline bool operator ==(foo<T> lhs, foo<T> rhs) { return true; }

int main() {
    foo<int> p = nullptr;
    assert(p == nullptr);
}

エラーメッセージでコンパイルに失敗します

foo.cpp:18:5:エラー:''の''に一致しませんfoo.cpp operator==: 18:5:注:候補は: foo.cpp:14:13:注: foo.cpp:14:13:注:テンプレート引数の推定/置換に失敗しました: foo.cpp:18:5:注:タイプ' 'と' ' の不一致p == nullptr

template<class T> bool operator==(foo<T>, foo<T>)

foo<T>std::nullptr_t

ただし、代わりにクラス内の定義を使用すると、コードは期待どおりに機能します。

エラーメッセージを理解しているとしましょう。テンプレート引数Tは、のタイプに対して推測できませんnullptr(ちなみに、decltype(*nullptr)コンパイルされません)。さらに、これはクラス内で関数を定義するだけでここで修正できます。

ただし、統一性の理由から(外部で定義する必要のある他のフレンド関数があります)、この関数をクラスの外部で定義したいと思います。

関数のクラス外定義を機能させるための「トリック」はありますか?

4

2 に答える 2

5

あなたのための3つの可能なオプションがあります

  • rhsのタイプを使用して新しいフレンド関数を宣言および定義します。std::nullptt_t

inline bool operator ==(foo<T> lhs, std::nullptr_t rhs) { return true; }
  • または、変数をと同等にする場合は、次nullptrのタイプを明示的に記述します。nullptr

assert(p == foo<int>(nullptr));
  • rhsのタイプを使用して新しいフレンド関数を宣言および定義します。void *

inline bool operator ==(foo<T> lhs, void *rhs) {         
    if (rhs == nullptr) 
        return true; 
    else
        return false;
    }
于 2012-04-29T20:35:41.403 に答える
3

Abhijitはすでに基本的な解決策を示していますが、これは興味深い問題なので、少し詳しく説明したいと思います。

次のように、テンプレートクラス内でフレンド関数を宣言する場合:

template <typename T>
struct A {
  friend void f(A);
};

次に、Aをパラメーターとして受け取るfと呼ばれる非テンプレート関数は、Aのフレンドになるということです。したがって、これらの関数を個別に定義する必要があります。

inline void f(A<int>) {...}
inline void f(A<float>) {...}
// etc.

クラス内で定義するのはショートカットですが。

この場合、フレンドであるのは非テンプレート関数であるとすでに述べているため、すべてのTのフレンドf(A)を定義するテンプレートを作成する方法はありません。非テンプレート関数は、コンパイラが一致する関数を探すときにテンプレート関数よりも多くの変換を許可するため、例で使用できるようにするのは非テンプレート関数であるという事実です。

少し面倒ですが、かなり一般的な回避策があります。nullptrを処理する他のテンプレート関数、またはnullptrにスローする可能性のあるその他の関数を定義し、それをメイン関数に延期することができます。

template <typename T>
inline bool operator ==(foo<T> lhs, std::nullptr_t rhs)
{
  return lhs==foo<T>(rhs);
}

あなたは対称性のためにそれを両方の方法でやりたいかもしれません:

template <typename T>
inline bool operator ==(std::nullptr_t lhs,foo<T> rhs)
{
  return foo<T>(lhs)==rhs;
}

別の注意点として、フレンド関数を定義した方法により、 UとTが同じタイプでない場合でもoperator==(foo<U>,foo<U>)フレンドが作成されます。foo<T>実際にはそれほど大きな違いはないでしょうが、これを行うには技術的に優れた方法があります。これには、テンプレート関数を前方に宣言してから、テンプレートパラメーターの特殊化を友だちにすることが含まれます。

完全な例を次に示します。

template <typename> struct foo;

template <typename T>
inline bool operator==(foo<T> lhs,foo<T> rhs);

template <typename T>
struct foo {
    foo(std::nullptr_t) { }

    friend bool operator==<>(foo lhs,foo rhs);
};

template <typename T>
inline bool operator ==(foo<T> lhs,foo<T> rhs)
{
  return true;
}

template <typename T>
inline bool operator ==(foo<T> lhs, std::nullptr_t rhs)
{
  return lhs==foo<T>(rhs);
}

template <typename T>
inline bool operator ==(std::null_ptr_t lhs,foo<T> rhs)
{
  return foo<T>(lhs)==rhs;
}

int main() {
    foo<int> p = nullptr;
    assert(p == nullptr);
    assert(nullptr == p);
    assert(p == p);
}
于 2012-04-30T04:13:19.507 に答える