C++ 使用 Template 時 沒有定義operator

==============程式碼如下==============

struct MyStruct {

int value;

} ; // struct MyStruct

template <class CType>

bool compare( CType a, CType b ){

return a > b;

} // bool compare()

int main(){

MyStruct a, b;

compare( 1, 2 );

compare( a, b );

} // main()

==============程式碼如上==============

我使用compare( 1, 2 )的時候沒有問題

但是我使用compare( a, b )的時候

compiler 說MyStruct 沒有定義 operator >

我知道為什麼錯誤,也知道幫MyStruct overload operator 就可解決了

可是我想知道有沒有方法可以不用overload operator

我希望能夠比較a和b的address 來處理

簡單描述一下我的問題 :

template <class CType>

bool compare( CType a, CType b ){

return a > b;

} // bool compare()

有沒有辦法在處理 int, float, double 等基本型態時,以一般方式比較

( int a = 1; int b = 2; ---->> return a > b; )

而在處理 Object 等非基本型態時,以其位址比較

( MyStruct a, b; ------>> return &a > &b

Update:

=====================================

感謝【帕拉提斯】大的回答,不過我希望的是像意見中novus 大所說的

不希望逐一定義 operator

還是謝謝,下次我會問的清楚些~

Update 2:

============================

To N大 : 其實我打意見原本也想要希望N大能夠回答一下,不過打完我的問題就忘記了.....

traits 的部分大至上看得懂,不過就N大說的 如果 MyStruct 中使用者有自定義 operator > 的話

似乎還是會單純比較位址,這並不是我想要的。( 我發問時沒想到這個狀況... )

另外 SFINAE 的部分 我實在看不懂,我不是很喜歡寫我看不懂的程式碼..而且N大也不建議

再者我也覺得用這個好像不是很親切.......還有我用的環境是VC 2008...所以不考慮使用這個...

Update 3:

最後看到萬用的...這似乎比較符合我這個新手的水準...

不過還是有些問題,我對 namespace 比較不熟,不過這個我應該可以解決....跳過

想請問的是

這個看起來比實作一個 operator> 比較位址的 CRTP base class

方便而且實用(?)多了

不知道這樣做有什麼缺點,用CRTP會比這個好的地方

Update 4:

==========================

To 【帕拉提斯】:

感謝帕大的回答

帕大的補充回答跟N大的最後一個萬用的方式好像一樣...

不過N大說了

"所有帶有明確型別的 operator> 的優先順序都會比這個高,只有在找不到更好的匹配情況下才會使用這個版本"

所以似乎不用再然後逐一特化 PoD type。

而且逐一特化好像也挺麻煩的....

感謝帕大回答。可能的話我希望程式碼能夠簡單一點,親切一點。

3 Answers

Rating
  • novus
    Lv 6
    9 years ago
    Favorite Answer

    略作補充:

    1. 雖然你不喜歡逐一定義 operator>,不過我還是建議你考慮一下 mixin 的作法,也就是定義一個 operator> 比較位址的 CRTP base class,讓需要比較位址的class 去繼承。

    其實我更喜歡用 macro 實作這種單純的 mixin

    2. std::tr1 的 type_traits header 提供了一些分辨複合型態和基礎型態的方法

    3. 在 C++ 03 底下要真正作到偵測 operator 其實有點小複雜。你可以搜尋一下SFINAE

    2011-10-22 23:30:24 補充:

    Wikipedia 對 SFINAE 的介紹

    http://en.wikipedia.org/wiki/SFINAE

    2011-10-23 11:26:25 補充:

    雖然 CRTP 在這個應用並非必要,但CRTP可以進一步限制操作的是衍生型別而非 Base型別。如果使用非CRTP Base,那麼兩個繼承自相同 Base 的衍生型別將可以互相操作,這樣的效果不見得是我們想要的。

    mixin 不一定要用繼承的,簡單的狀況用macro似乎也不壞,例如

    1. boost 的 noncopyable 使用繼承 (這就不需要 CRTP)

    2. google 的 DISALLOW_COPY_AND_ASSIGN 用macro實作

    兩者的功能完全相同

    2011-10-24 19:58:12 補充:

    並不是這樣,traits 通常會搭配 template 型別推導機制使用,事實上 trait 應該都不可能在預處理器裡面使用,因為預處理器會在編譯之前就作用完畢,然而 template 的型別要等到語意分析才有可能得知。

    我也不是很鼓勵你用第三點說....

    2011-10-24 20:56:23 補充:

    本來只是提示一下搜尋關鍵字,結果你害我多打好多字,不回答一下實在不划算。

    假如使用 Compare 的 class 不多,而且原始碼都在你的掌控之下,我會覺得 mixin 是一個好作法,請參考意見。但是像string、vector就不能讓你做侵入式的修改

    接下來講一下 tr1 type_traits,太舊的編譯器可能不支援,支援的編譯器 include 位址也不太一樣,我比較建議你去裝boost。判斷一個東西是否為 class 可參考下面的程式碼:

    #if defined(_MSC_VER)

    #include <type_traits>

    #elif defined(__GNUC__)

    #include <tr1/type_traits>

    #endif // 其他的請自己查吧.....

    struct MyClass { int data; };

    enum MyEnum { FOO, BAR };

    std::cout << std::tr1::is_class<int>::value;

    std::cout << std::tr1::is_class<std::string>::value;

    std::cout << std::tr1::is_class<MyClass>::value;

    std::cout << std::tr1::is_class<MyEnum>::value;

    輸出會是 0110

    Compare可以這樣寫,這大概不是很完善的實作,不過大致上可以用

    template <bool isClass>

    struct Comparator {

    template <typename T>

    static bool GreaterThen(T a, T b) {

    return a > b;

    }

    };

    template <>

    struct Comparator<true> {

    template <typename T>

    static bool GreaterThen(const T& a, const T& b) {

    return &a > &b;

    }

    };

    template <typename T>

    inline bool Compare(const T& a, const T& b) {

    return Comparator<std::tr1::is_class<T>::value>::GreaterThen(a, b);

    }

    以上只是根據物件是否為 class 型別來判斷,並不太精準。有很多class本身就有提供比較運算子。更精確的話應該要檢查 > expression 是否成立,不論這個 expression 是天然的、使用者定義的、靠轉型獲得的。類似這樣的檢測可以靠 SFINAE 實現。

    template <typename T>

    struct is_gt_comparable

    {

    typedef char yes[1];

    typedef char no[2];

    template <typename U>

    static U make();

    template <typename U>

    static yes& test( int (*)[sizeof(make<U>() > make<U>())] );

    template <typename U>

    static no& test(...);

    static const bool value = sizeof(test<T>(NULL)) == sizeof(yes);

    };

    我刻意寫成這個形式,即使你不明白運作原理也可以比照上面 std::tr1::is_class 的用法。但是非常不幸的,這樣的寫法在某些編譯器會得出錯誤的結果,包括VC 2008。

    還有最後一個方法,你可以提供一個萬用的

    namespace compare_impl {

    template <typename T>

    bool operator>(const T& lhs, const T& rhs) {...}

    }

    請務必用 namespace 保護好以免隨著標頭檔到處蔓延成災。所有帶有明確型別的 operator> 的優先順序都會比這個高,只有在找不到更好的匹配情況下才會使用這個版本,而且這個版本的 operator> 在 namespace之外就無作用了。

    2011-10-24 23:03:45 補充:

    我應該再提示多一點

    全形括號自己改一下吧

    namespace compare_private {

    template <typename T>

    bool operator>(const T& lhs, const T& rhs) {

    return &lhs > &rhs;

    }

    template <typename T>

    bool CompareImpl(const T& lhs, const T& rhs) {

    return lhs > rhs;

    }

    }

    2011-10-24 23:04:08 補充:

    template <typename T>

    inline bool Compare(const T& lhs, const T& rhs) {

    return compare_private::CompareImpl(lhs, rhs);

    }

    MyClass a, b;

    cout << Compare(a, b) << endl;

    cout << (a > b) << endl; //<-- 編譯失敗

    2011-10-24 23:08:27 補充:

    SFINAE 也是可以用的,我最後面這個方法就是為了讓VC 2008可以接受第三種作法而想出來的work around。

    只是想出來之後覺得做成 trait 有點多此一舉。其他的你先想想看,今天我要休息了

  • PGCafe
    Lv 5
    9 years ago

    感謝novus大的意見

    n大意見比較符合我所想要的解答,但是稍微有些疑問

    我查了一下 CRTP base class 基本架構如下

    template

    struct Base{ ... }

    struct MyStruct : Base { ... }

    而N大的建議應該是將operator > 寫在 struct Base 裡面

    2011-10-23 10:52:01 補充:

    想請問一下這跟寫一個單純的Object Class然後再繼承這個Object class有什麼不同

    大概如下

    class Object{ ... } // operator > 寫在這裡面

    struct MyStruct : Object { ... }

    這樣似乎也能夠做到一樣的事情

    ====================

    關於2跟3點還在研究中~~

    ( Post 意見似乎不會在我的知識+那邊提式有新意見...希望N大能回來看看... )

    2011-10-23 15:42:26 補充:

    謝謝N大解答,CRTP部份大至上了解,剩下可能要用經驗來彌補了...

    關於第二點提到的 type_traits header

    我查到的資料似乎是利用compiler有沒有編譯錯誤來判斷是什麼類型

    如果確實是這樣的話..那我是不是應該用

    #if ( 是基礎類型 )

    compare 基礎類型

    #else

    compare 其他類型

    #endif

    這樣

    ==============

    至於第三點有些不是能理解,有機會來再研究...

  • 其威
    Lv 7
    9 years ago

    你可以:

    複寫 MyStruct::operator >()

    class MyStruct

    {

    ...

    public:

    bool operator > (MyStruct & rhs)

    {

    return this > &rhs;

    }

    }

    但是你說了要找這個方法以外的方法,所以請接著看下面幾項。撰寫 global 的 operator > ( MyStruct &, MyStruct & ):

    bool operator > ( MyStruct & a, MyStruct & b )

    {

    return &a > &b;

    }

    這樣呼叫 compare() 的時候,它會呼叫 global 的 operator > 來比較 a 與 b。多載 compare() 函式:

    bool compare( MyStruct & a, MyStruct & b)

    {

    return &a > &b;

    }

    這樣在解析 compare() 的時候,會發現更符合 signature 的函式,於是放棄使用汎化版本。特化 template 函式 compare():

    template <>

    bool compare< MyStruct >( MyStruct & a, MyStruct & b )

    {

    return &a > &b;

    }

    這樣在解析 compare() 的時候,會發現針對 MyStruct 的特化版本,於是直接使用之。

    2011-10-24 10:11:28 補充:

    我想還有個辦法,那就是把 template 汎化成 &a > &b,然後逐一特化 PoD type。

    template < typename CType >

    bool compare( CType & a, CType & b )

    {

    return &a > &b;

    }

    template < >

    bool compare< int >( int & a, int & b )

    {

    return a > b;

    }

    // 以及 char、short、long、long long、unsigned 版本、float、double、long double...

Still have questions? Get your answers by asking now.