2010-05-21 60 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) {} 
    }; 
};