2010-05-21 78 views
2

我一直在编写几个包含嵌套迭代器类的类模板,需要进行相等比较。我认为这是相当典型的,这种比较是通过非会员(和非朋友)operator==函数来执行的。在这样做的时候,我的编译器(我使用的是Mingw32 GCC 4.4,标志为-O3 -g -Wall)未能找到该函数,并且我用尽了可能的原因。嵌套模板类的C++非成员函数

在下面相当大的代码块中有三个类:一个Base类,一个持有Base对象的Composed类和一个与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; 
} 
+0

此问题似乎是关于在C++中的模板名称查找。我试图添加一个名称查找标签,因为这看起来是一个相当常见的子主题,但作为一个新手我缺乏声誉。谁会更有信誉,而不是增加这样的标签呢? – beldaz 2010-05-22 05:42:21

回答

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在自由方法的参数(不适用于结果类型)是一个红色的鲱鱼,似乎阻止自动论证扣除。

+0

感谢Matthieu - 我要回答你的回答,这个回答很清楚,写得很好。我希望标准有一个严格的规则或指导可能更具确定性,但它看起来像你的经验法则一样好。这有点令人不满意,但是语言,而不是你的答案。 – beldaz 2010-05-22 04:38:06

+0

很抱歉,我不是standardista,这意味着我通常不会在每次需要它时引用标准:)这里有些成员像AndreyT可能会找到确切的报价,如果它存在,但没有真正的方法可以直接吸引他们的注意力短小对他们对另一个问题的回答发表评论......这是非常不礼貌的。我希望进行私人消息传递;) – 2010-05-22 12:36:05

0

如果您没有重载运算符&,那么只需比较内存地址。没有必要这样做。

+0

如果比较内存地址与平等比较可能是微不足道的,那么我认为我们很快就会明白。 – 2010-05-21 12:47:21

+0

我正在寻找对问题的洞察力,而不是快速的解决方法。另外,在迭代器中,最重要的比较是序列的end(),对此,内存地址比较通常是不合适的。 – beldaz 2010-05-21 13:08:38

0

接受答案后,我考虑了如何以最少的努力来修复我的代码。有了这个问题的更清晰的概念,我从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) {} 
    }; 
};