2013-03-01 40 views
3

假設我有barfoo類型。我怎樣才能建立一個模板類has_call_with_arg<>這樣has_call_with_arg<bar,foo>::value爲真,當且僅當如何檢查會員運營商(類型)?

bar b; 
foo f; 
b(f); 

將彙編?我看着各種相關問題(包括上面提到的),並試圖

template<typename Func, typename Arg> 
class has_call_with_arg 
{ 
    struct bad {}; 
    struct test : Func 
    { 
    template<typename C> 
    bad operator()(C const&r); 
    }; 
public: 
    static const bool value = 
    !std::is_same<bad, typename std::result_of<test(Arg const&)>::type >::value; 
}; 

,但沒有工作(沒有檢測到正確的匹配)。怎麼了?

+0

-1 linkdumping。 – Puppy 2013-03-01 18:03:24

+3

+1只是因爲這隻傻小狗。 – Abyx 2013-03-01 18:04:43

+0

不是一個真正名副其實的成員職能和會員運營商通常是不同的交易。 – Puppy 2013-03-01 18:06:17

回答

3
template<typename sig, typename functor> struct is_callable; 
template<typename Ret, typename... Arg, typename functor> 
struct is_callable<Ret(Arg...), functor> { // partial spec 
private: 
    struct no {}; 
public: 
    template<typename U> static auto f(std::nullptr_t) -> decltype(std::declval<U>()(std::declval<Arg>()...)); 
    template<typename U> static no f(...); 
    static const int value = std::is_convertible<decltype(f<functor>(nullptr)), Ret>::value; 
}; 

我創建該內容for my tutorials,這解釋此特徵的結構(非可變參數形式在前)。

+0

不錯。我有一個後續問題:我們是否真的需要'f'的第一個重載有一個trailling返回類型?在一個簡單的測試案例中,我嘗試過使用標準(即沒有trailling)返回類型。我想知道是否存在陷阱返回類型避免的陷阱。 – 2013-03-01 23:39:16

2

std::result_of<test(Arg const&)>提供模板參數,該參數是函數類型,參考Arg const並返回test。所以它的typetest,不太有幫助。

需要注意的是書面的,需要的代碼

bar b; 
foo f; 
b(f); 

是有效的,真是5個要求:barfoo每個可訪問的默認構造函數和析構函數,以及b(f)是有效的表達式。我將專注於最後一個(這也許就是你的意思)。如果您確實是指其他部分,則可以使用標準<type_traits>屬性添加這些部分。

功能std::declval非常適合假裝你有一個給定類型的對象,即使你沒有。它絕對不能被調用,所以它通常只用在decltype表達式中。

有兩種基本的方法來拉斷SFINAE技巧的基礎上,兩個地C++允許模板參數推導失敗只是放棄失敗的聲明:

首先,嘗試匹配一類偏專業化:

template<typename Func, typename Arg, typename Enable = void> 
struct has_call_with_arg1 : public std::false_type {}; 

template<typename Func, typename Arg> 
struct has_call_with_arg1<Func, Arg, 
    decltype(std::declval<Func&>()(std::declval<Arg&>()))> 
    : public std::true_type {}; 

其次,至少有一個重載是功能模板時的重載分辨率。 (一類模板的非模板成員函數不會在這裏工作,因爲實例化類需要每個成員的聲明是有效的。)

namespace has_call_with_arg_impl { 
    template<typename F, typename A> 
    std::true_type test(decltype(std::declval<F&>()(std::declval<A&>()))*); 

    template<typename F, typename A> 
    std::false_type test(...); 
} 

template <typename Func, typename Arg> 
struct has_call_with_arg2 
    : public decltype(has_call_with_arg_impl::test<Func,Arg>(nullptr)) {}; 

演示:http://ideone.com/KgRI8y

+1

第二個實現對我來說似乎是正確的,但如果可調用類型返回的類型不是「void」,則第一個實現不起作用。例如,對於'struct F {int operator()(double){return 0; }};'我們有'has_call_with_arg1 :: value == false'。確實,'has_call_with_arg1'有3個參數,但是第3個參數'Enable',有一個默認的'= void'。只提供2個參數時,第三個參數將獲取默認值,並選擇主模板。但是,'has_call_with_arg1 :: value == true'因爲專業化比主要匹配更好。 – 2013-03-01 23:36:52

+0

好的。也許這就是爲什麼我通常使用函數的原因。但我認爲有一些有用的技巧涉及默認模板參數'typename Enable = void' .... – aschepler 2013-03-02 00:33:23