3

私は、等値比較が必要なネストされた反復子クラスを含むいくつかのクラス テンプレートを作成しています。かなり典型的だと思いますが、比較は非メンバー (および非フレンド)operator==関数で実行されます。そうすることで、私のコンパイラ (Mingw32 GCC 4.4 with flags を使用し-O3 -g -Wallています) が関数を見つけられず、考えられる理由がなくなりました。

以下のかなり大きなコード ブロックには、3 つのクラスがあります。Base クラス、Base オブジェクトを保持する Composed クラス、および Outer クラス内にネストされている点を除いて Composed クラスと同一の Nested クラスです。非メンバーoperator==関数はそれぞれに提供されます。これらのクラスは、テンプレート化された形式とテンプレート化されていない形式 (それぞれの名前空間内) にあり、後者は符号なし整数に特化した前者と同等です。

ではmain、各クラスの 2 つの同一オブジェクトが比較されます。テンプレート化されていない場合は問題ありませんが、テンプレート化されている場合、コンパイラは を見つけられませんoperator==。どうしたの?

#include <iostream>

namespace templated {

template<typename T>
class Base {
  T t_;
public:
  explicit Base(const T& t) : t_(t) {}

  bool
  equal(const Base& x) const {
    return x.t_==t_;
  }
};

template<typename T>
bool
operator==(const Base<T> &x, const Base<T> &y) {
  return x.equal(y);
}

template<typename T>
class Composed {
  typedef Base<T> Base_;
  Base_ base_;
public:
  explicit Composed(const T& t) : base_(t) {}
  bool equal(const Composed& x) const {return x.base_==base_;}
};

template<typename T>
bool
operator==(const Composed<T> &x, const Composed<T> &y) {
  return x.equal(y);
}

template<typename T>
class Outer {
public:
  class Nested {
    typedef Base<T> Base_;
    Base_ base_;
  public:
    explicit Nested(const T& t) : base_(t) {}
    bool equal(const Nested& x) const {return x.base_==base_;}
  };
};

template<typename T>
bool
operator==(const typename Outer<T>::Nested &x,
    const typename Outer<T>::Nested &y) {
  return x.equal(y);
}

} // namespace templated

namespace untemplated {

class Base {
  unsigned int t_;
public:
  explicit Base(const unsigned int& t) : t_(t) {}

  bool
  equal(const Base& x) const {
    return x.t_==t_;
  }
};

bool
operator==(const Base &x, const Base &y) {
  return x.equal(y);
}

class Composed {
  typedef Base Base_;
  Base_ base_;
public:
  explicit Composed(const unsigned int& t) : base_(t) {}
  bool equal(const Composed& x) const {return x.base_==base_;}
};

bool
operator==(const Composed &x, const Composed &y) {
  return x.equal(y);
}

class Outer {
public:
  class Nested {
    typedef Base Base_;
    Base_ base_;
  public:
    explicit Nested(const unsigned int& t) : base_(t) {}
    bool equal(const Nested& x) const {return x.base_==base_;}
  };
};

bool
operator==(const Outer::Nested &x,
    const Outer::Nested &y) {
  return x.equal(y);
}

} // namespace untemplated

int main() {
  using std::cout;
  unsigned int testVal=3;
  { // No templates first
    typedef untemplated::Base Base_t;
    Base_t a(testVal);
    Base_t b(testVal);

    cout << "a=b=" << testVal << "\n";
    cout << "a==b ? " << (a==b ? "TRUE" : "FALSE") << "\n";

    typedef untemplated::Composed Composed_t;
    Composed_t c(testVal);
    Composed_t d(testVal);

    cout << "c=d=" << testVal << "\n";
    cout << "c==d ? " << (c==d ? "TRUE" : "FALSE") << "\n";

    typedef untemplated::Outer::Nested Nested_t;
    Nested_t e(testVal);
    Nested_t f(testVal);

    cout << "e=f=" << testVal << "\n";
    cout << "e==f ? " << (e==f ? "TRUE" : "FALSE") << "\n";
  }
  { // Now with templates
    typedef templated::Base<unsigned int> Base_t;
    Base_t a(testVal);
    Base_t b(testVal);

    cout << "a=b=" << testVal << "\n";
    cout << "a==b ? " << (a==b ? "TRUE" : "FALSE") << "\n";

    typedef templated::Composed<unsigned int> Composed_t;
    Composed_t c(testVal);
    Composed_t d(testVal);

    cout << "c=d=" << testVal << "\n";
    cout << "d==c ? " << (c==d ? "TRUE" : "FALSE") << "\n";

    typedef templated::Outer<unsigned int>::Nested Nested_t;
    Nested_t e(testVal);
    Nested_t f(testVal);

    cout << "e=f=" << testVal << "\n";
    cout << "e==f ? " << (e==f ? "TRUE" : "FALSE") << "\n";
    // Above line causes compiler error:
    // error: no match for 'operator==' in 'e == f'
  }

  cout << std::endl;
  return 0;
}
4

3 に答える 3

5

この問題は、テンプレートを使用するネストされたクラスではかなり一般的です。

template <class T>
struct Outer { struct Inner {}; };

template <class T>
void increment(typename Outer<T>::Inner&) {}

increment関数が見つかりません。ルックアップはコンパイラーが解決するには難しすぎると思います。

ただし、問題を軽減することはできますが、

namespace detail
{
  template <class T> struct InnerImpl {};

  template <class T> void increment(InnerImpl& ) {}
}

template <class T>
struct Outer
{
  typedef detail::InnerImpl<T> Inner;
};

int main(int argc, char* argv[])
{
  Outer<int>::Inner inner;
  increment(inner);         // works
}

おかしいですね。

経験則としてtypename、freeメソッドの引数(結果タイプではない)には赤いニシンがあり、自動引数推論を妨げているようです。

于 2010-05-21T12:58:23.680 に答える
1

回答を受け入れた後、最小限の労力でコードを修正する最善の方法を考えました。問題のより明確なアイデアを武器に、C++ FAQから新しいインスピレーションを得て、非メンバーoperator==をフレンド関数としてクラス定義にマージしました。メンバー関数を提供する理由はフレンドの必要性を回避することだったので、これは思ったほどハックではありませんequal()が、テンプレートの場合、フレンド関数には関数定義をクラス内に保持できるという利点があります。これにより、ルックアップの問題を回避できます。

template<typename T>
class Outer {
public:
  class Nested {
    typedef Base<T> Base_;
    Base_ base_;
    friend bool operator==(Nested const &x, Nested const &y) {
      return x.base_==y.base_;
    }
  public:
    explicit Nested(const T& t) : base_(t) {}
  };
};
于 2010-05-22T05:32:11.560 に答える
-1

operator& をオーバーロードしていない場合は、メモリ アドレスを比較してください。これを行う必要はありません。

于 2010-05-21T12:35:32.833 に答える