2014-02-22 45 views
1

我遇到了一個基於參數包的特性啓用/禁用重載的問題。這是我想回答的另一個問題。我有一個靜態的go()函數,如果所有的Mixins類型都有一個靜態的check()方法,應該調用它。同樣,應該有另一種靜態go()方法,如果Mixins有一個check()方法應該被調用。我試圖創造條件讓基於這一政策重載,但由於某些原因,當我把它叫做它選擇第二過載,它試圖撥打handle()如何正確處理這個SFINAE?

template<class T, class...> 
using first = T; 

template<class... Mixins> 
struct Checker 
{ 
public: 
    template<class = 
      first<void, 
        typename std::enable_if<has_check<Mixins>::value>::type...>> 
    static void go() 
    { 
     auto x{ (Mixins::check(), 0)... }; 
     (void)x; 
    } 

    static void go() 
    { 
     auto x{ (Mixins::handle(), 0)... }; 
     (void)x; 
    } 
}; 

struct A { static void check() { std::cout << "check() "; } }; 
struct B { static void check() { std::cout << "check() "; } }; 

int main() 
{ 
    Checker<A, B>::go(); 
} 

Here is a demo.

我得到一個錯誤說A沒有任何成員名字handle()。這一直在困擾着我,但我無法找到解決辦法。我如何解決這個問題?如何根據參數包正確協調重載分辨率?

+0

無關:不知道你是否在意,但是當'Mixins'爲空時,你的'auto x {...}'行會出現編譯錯誤。 'auto x {};'是不合格的。通常的解決方法是在初始化程序中添加一個額外的前導0:'auto x {0,(Mixins :: check(),0)...};' – Casey

+0

@Casey感謝您指出這一點。下面的'0'也很重要嗎? – 0x499602D2

+0

是的。一個支撐初始化器列表是一個方便的地方來擴展參數包,以保證按順序評估。此代碼使用我稱之爲「初始化程序列表技巧」來評估任意包擴展表達式 - 在本例中爲'Mixins :: check()'和/或'Mixins :: handle()' - 將其用作副作用在構建一個加載的初始整數列表。擴展中每個出現的'(Mixins :: check(),0)'評估一個'check'函數,並且 - 使用逗號運算符 - 拋出結果並在初始化列表中粘貼一個「0」。 – Casey

回答

1

你沒有什麼區分go兩種口味。兩種解決方案都可行時,重載解析將優先使用非模板goThe ... vs. int technique works

template<class... Mixins> 
struct Checker 
{ 
private: 
    template<class = 
      first<void, 
        typename std::enable_if<has_check<Mixins>::value>::type...>> 
    static void foo(int) 
    { 
     auto x{ (Mixins::check(), 0)... }; 
     (void)x; 
    } 

    static void foo(...) 
    { 
     auto x{ (Mixins::handle(), 0)... }; 
     (void)x; 
    } 

public: 
    static void go() { 
     foo(0); 
    } 
}; 

由於foo兩個重載是可行的通話foo(0),但foo(int)foo(...)更好的匹配,使重載決議會喜歡它,當不SFINAEed了它。

編輯:我在這裏犯了一個錯誤,我認爲

template<class = 
     first<void, 
       typename std::enable_if<has_check<Mixins>::value>::type...>> 

可以正確SFINAE遠的foo過載。但是,此處的錯誤不是替換失敗,因爲未命名的函數模板參數的默認值不取決於函數模板的參數。通常情況下,當你有參數的類型T一類,你要約束基於對T某些屬性成員函數f,將強制使用的別名爲依賴T

template <class T> 
struct foo { 
    template <class U=T, 
      class=typename std::enable_if<std::is_integral<U>::value>::type> 
    void f(); 
}; 

同樣的伎倆不會在OP的情況下工作,但是,由於OP的類是在一個包上參數化的,並且沒有辦法爲參數包提供默認值。我們必須棘手:

template<class T = void, 
     class = first<T, typename std::enable_if<has_check<Mixins>::value, T>::type...>> 
static void foo(int) 
{ 
    auto x{ (Mixins::check(), 0)... }; 
    (void)x; 
} 

迫使雙方first<...>enable_if<...>擴展爲SFINAE有效。 See it at Coliru.

+0

我試圖使用這樣的東西,但我想我在實現時犯了一些錯誤。無論如何,我感謝你的幫助。我將使用這個。 :) – 0x499602D2

+0

非常好!感謝你的幫助! :) – 0x499602D2