C ++ не-функции для вложенных шаблонных классов - PullRequest
2 голосов
/ 21 мая 2010

Я написал несколько шаблонов классов, которые содержат вложенные классы итераторов, для которых требуется сравнение на равенство. Как я полагаю, это довольно типично, сравнение выполняется с функцией не-членом (и не-другом) operator==. При этом мой компилятор (я использую Mingw32 GCC 4.4 с флагами -O3 -g -Wall) не может найти функцию, и у меня закончились возможные причины.

В довольно большом блоке кода ниже есть три класса: базовый класс, класс Composed, содержащий базовый объект, и класс Nested, идентичный классу Composed, за исключением того, что он вложен в класс Outer. Номера для operator== функции предоставляются для каждого. Эти классы представлены в шаблонной и нетэмплированной форме (в своих соответствующих пространствах имен), причем последний эквивалентен первому, специализированному для целых чисел без знака.

В main сравниваются два идентичных объекта для каждого класса. Для незаполненного случая проблем нет, но для шаблонного случая компилятору не удается найти 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;
}

Ответы [ 3 ]

5 голосов
/ 21 мая 2010

Проблема довольно распространена для вложенного класса с шаблонами.

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 в аргументах (не для типа результата) бесплатного метода представляет собой красную сельдь и, по-видимому, предотвращает автоматический вывод аргументов.

0 голосов
/ 22 мая 2010

После принятия ответа я подумал о том, как лучше всего исправить мой код с минимальными усилиями. Вооружившись более ясным представлением о проблеме, я черпал новое вдохновение из 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) {}
  };
};
0 голосов
/ 21 мая 2010

Если вы не перегружены оператором &, просто сравните адреса памяти. Там нет необходимости делать это.

...