這是一位網友出的問題:要如何偵測某個型別是否可以套用大於運算式( > expression )呢?這裡假設是在 C++ 03 的標準之下。

熟悉 C++ 的網友應該都會想到可以靠 SFINAE 手法解決這類偵測問題。仿照 tr1 type_traits 的使用風格,典型的寫法大概像這樣:

template <typename T>
struct is_gt_comparable
{
    typedef char yes;
    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(no);
};

struct MyClass { int data; };

cout << is_gt_comparable<int>::value << endl;
cout << is_gt_comparable<string>::value << endl;
cout << is_gt_comparable<MyClass>::value << endl;

在 g++ 4.5 完全如預期般的輸出 110,在 VC++ 2008 雖然可以編譯,但是輸出結果是 111。至於 VC 為何會認為 NULL 可以轉換為一個不存在型別的指標,就不是我可以理解的了。稍加修改避開指標類型:

...

template <int N>
struct helper
{
    helper(int n);
    char a[N];
};

template <typename U>
static yes& test( helper<sizeof(make<U>() / make<U>())> );

...

做了上述修改後,好消息是 VC 總算可以正確偵測 + - * / % 之類的 expression 了!但是遇到 > < == 之類的 expression 仍然掛點,原因不免令人好奇 -- VC 似乎在無法充分檢查型態的情況下仍然假設 sizeof(make<U>() > make<U>())為 1!

於是我又做了一些修改,包括從 return type 方面下手,但都不成功。而且我並不那麼有把握這些完全合乎 C++ 03 標準,我甚至懶得查,畢竟在廣泛使用的編譯器上失敗就不算是令人滿意的實用方法。

要設計 VC 也可以接受的 test() 函數 signature 看起來不是那麼簡單,那麼換個方式來實作 SFINAE 吧。我想到還可以從 operator> 下手,像下面這種濫好人 operator> 一定要等到世界上沒有別的男人可以選才會獲得青睞

namespace compare_private
{
    typedef char fail[777];

    template <typename T>
    fail& operator>(const T& lhs, const T& rhs);

    template <typename T>
    struct test {
        static T makeT();
        static const bool value = sizeof(makeT() > makeT()) != sizeof(fail);
    };
} // namespace compare_private

template <typename T>

struct is_gt_comparable
{
    static const bool value = compare_private::test<T>::value;
};

經測試在 VC++ 和 g++ 都跑出和預期一樣的結果。上面的 sizeof 是很破的招式,因為我們沒有辦法排除異想天開的使用者碰巧自訂了 operator> 傳回 size 為 777 的東西。在 C++ 11 當然要用 decltype 和 std::is_same (tr1 有) 比較才正確,不過要是有 decltype 可用,這個問題就不需要用這麼彆扭的方式解決。

主要問題看似解決了,但還是不能讓人滿意。雖然這個濫好人 operator> 的 overloading 順位比其他所有的 operator> 實作都還要低,但是比起「先轉型再套用 operator」還是高了那麼一點點,所以無法偵測經由轉型而獲得的 > expression,第一種方法就沒有這方面的問題。

我沒有美國時間繼續探索下去了,畢竟這不是我遇到的問題,有沒有網友要來解救一下呢?

novus 發表在 痞客邦 PIXNET 留言(0) 人氣()