2011-04-24 63 views
11

我想要幾個重載的全局函數to_string()採用某種類型T並將其轉換爲其字符串表示形式。對於一般情況下,我希望能夠寫:使用SFINAE檢查全局運算符<<?

template<typename T,class OutputStringType> inline 
typename enable_if<!std::is_pointer<T>::value 
       && has_insertion_operator<T>::value, 
        void>::type 
to_string(T const &t, OutputStringType *out) { 
    std::ostringstream o; 
    o << t; 
    *out = o.str(); 
} 

我的has_insertion_operator實施至今:

struct sfinae_base { 
    typedef char yes[1]; 
    typedef char no[2]; 
}; 

template<typename T> 
struct has_insertion_operator : sfinae_base { 
    template<typename U> static yes& test(U&); 
    template<typename U> static no& test(...); 

    static std::ostream &s; 
    static T const &t; 

    static bool const value = sizeof(test(s << t)) == sizeof(yes); // line 48 
}; 

(它借用了thisthis) ,似乎工作。 但現在我希望有to_string對於做operator<<有自己to_string()成員功能類型,即重載版本:

template<class T,class OutputStringType> inline 
typename enable_if<!has_insertion_operator<T>::value 
       && has_to_string<T,std::string (T::*)() const>::value, 
        void>::type 
to_string(T const &t, OutputStringType *out) { 
    *out = t.to_string(); 
} 

has_to_string實現是:

#define DECL_HAS_MEM_FN(FN_NAME)          \ 
    template<typename T,typename S>          \ 
    struct has_##FN_NAME : sfinae_base {        \ 
    template<typename SignatureType,SignatureType> struct type_check; \ 
    template<class U> static yes& test(type_check<S,&U::FN_NAME>*); \ 
    template<class U> static no& test(...);       \ 
    static bool const value = sizeof(test<T>(0)) == sizeof(yes); \ 
    } 

DECL_HAS_MEM_FN(to_string); 

(這部分似乎工作正常,它改編自this。) 然而,當我有:

struct S { 
    string to_string() const { 
    return "42"; 
    } 
}; 

int main() { 
    string buf; 
    S s; 
    to_string(s, &buf); // line 104 
} 

我得到:

foo.cpp: In instantiation of ‘const bool has_insertion_operator<S>::value’: 
foo.cpp:104: instantiated from here 
foo.cpp:48: error: no match for ‘operator<<’ in ‘has_insertion_operator<S>::s << has_insertion_operator<S>::t’ 

好像SFINAE沒有發生。我如何正確書寫has_insertion_operator以確定全球operator<<是否可用?

僅供參考:我使用g ++ 4.2.1(在Mac OS X上作爲Xcode的一部分發布)。 另外,我希望代碼只能是沒有第三方庫的標準C++ 03,例如Boost。

謝謝!

+1

這一切都是可行的,但*爲什麼*? – Potatoswatter 2011-04-24 03:49:56

+2

@Patatoswatter:爲什麼不重要。請假定我的項目的其餘部分我知道我在做什麼。如果您必須知道,它是框架的一部分,用於傳遞任何類型的參數以形成本地化錯誤消息的一部分。這個問題沒有必要的所有細節。如果您知道如何操作,請回答問題。這將非常感激。 – 2011-04-24 03:53:40

+2

爲什麼總是很重要。 – GManNickG 2011-04-24 06:47:37

回答

10

我應該只是更忠實於this的答案。 的工作實現:

namespace has_insertion_operator_impl { 
    typedef char no; 
    typedef char yes[2]; 

    struct any_t { 
    template<typename T> any_t(T const&); 
    }; 

    no operator<<(std::ostream const&, any_t const&); 

    yes& test(std::ostream&); 
    no test(no); 

    template<typename T> 
    struct has_insertion_operator { 
    static std::ostream &s; 
    static T const &t; 
    static bool const value = sizeof(test(s << t)) == sizeof(yes); 
    }; 
} 

template<typename T> 
struct has_insertion_operator : 
    has_insertion_operator_impl::has_insertion_operator<T> { 
}; 

我相信它實際上依靠SFINAE。

+0

精彩:它在C++ 03編譯器中有效嗎? – 2013-03-23 09:58:57

+0

嘗試一下,找出答案。 – 2013-03-23 15:58:17

+0

感謝代碼無論如何:) – 2013-04-17 15:17:02

1

在線48上的value的初始值設定項不在SFINAE工作的上下文中。嘗試將表達式移至函數聲明。

#include <iostream> 

struct sfinae_base { 
    typedef char yes[1]; 
    typedef char no[2]; 
}; 

template<typename T> 
struct has_insertion_operator : sfinae_base { 

    // this may quietly fail: 
    template<typename U> static yes& test(
     size_t (*n)[ sizeof(std::cout << * static_cast<U*>(0)) ]); 

    // "..." provides fallback in case above fails 
    template<typename U> static no& test(...); 

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

但是,我不得不質疑進入這一領域的複雜程度。我看到非正交機制會相互磨合(to_stringoperator<<),並且我聽到糟糕的假設(例如operator<<是全局vs成員,儘管在這方面實現的代碼看起來不錯)。

+1

它不編譯有幾個錯誤。第一個錯誤是:沒有依賴於模板參數的「測試」參數,因此必須提供「測試」聲明。 – 2011-04-24 04:16:10

+0

順便說一句:operator <<必須是全局的,如果它是插入運算符,因爲第一個參數必須是ostream&。 – 2011-04-24 04:18:55

+0

順便說一句#2:ostream :: ostream()受到保護。 – 2011-04-24 04:22:25