2017-10-19 74 views
2

我正在使用VS2015更新3.我有一個函數,我希望根據可調用對象的返回類型進行專門化。當可調用對象是一個仿函數時,一切都按預期工作。當可調用對象是函數或函數指針時,它無法專門化重載函數。我覺得我失去了一些顯而易見的東西,但是我在一年之內沒有對SFINAE做過任何事情。未能專門爲函數指針功能

我錯過了什麼導致了專業化失敗?

template <typename T> 
struct S 
{ 
    T mOp; 
    template <typename = void> 
    typename std::enable_if< 
     std::is_same< 
      std::remove_cv_t< 
       std::remove_reference_t< 
        decltype(mOp()) 
       > 
      >, 
      void 
     >::value 
    >::type func() 
    { 
     std::cout << "bool" << std::endl; 
    } 
    template <typename = void> 
    typename std::enable_if< 
     std::is_same< 
      std::remove_cv_t< 
       std::remove_reference_t< 
        decltype(mOp()) 
       > 
      >, 
      bool 
     >::value 
    >::type func() 
    { 
     std::cout << "void" << std::endl; 
    } 
}; 

template <typename T> 
auto createS(T&& t) 
{ 
    return S<T>{ t }; 
} 

void vfunc() 
{ 
} 
bool bfunc() 
{ 
    return true; 
} 
struct vfunctor 
{ 
    void operator()() 
    { 
    } 
}; 
struct bfunctor 
{ 
    bool operator()() 
    { 
     return true; 
    } 
}; 

void func() 
{ 
    createS(bfunc).func();  // Fails to specialize func() 
    createS(vfunc).func();  // Fails to specialize func() 
    createS(vfunctor{}).func(); 
    createS(bfunctor{}).func(); 
} 

回答

5

無的這個作品,因爲替換故障只在替代的直接背景失敗 - 你正在試圖SFINAE在不依賴於直接的函數模板參數的上下文。您在func()中的約束基於模板參數,而不是本地函數模板參數,因此這些只是一個硬性錯誤。

最簡單的方法是通過標籤分派。裹decltype(mOp())成標籤類型,然後就過載:

template <class T> struct tag { }; 

template <typename T> 
struct S 
{ 
    T mOp; 

    void func() { 
     func_impl(tag<std::decay_t<decltype(mOp())>>{}); 
    } 

    void func_impl(tag<bool>) { std::cout << "bool\n"; }   
    void func_impl(tag<void>) { std::cout << "void\n"; }   
}; 

如果您需要func()是SFINAE友好本身出於某種原因,那麼你可以引入一個新的模板參數只是一個假象,淘汰原有一個:

template <class..., class U=T> 
auto func() 
    -> decltype(func_impl(tag<std::decay_t<std::invoke_result_t<U>>>{})) 
{ 
    return func_impl(tag<std::decay_t<std::invoke_result_t<U>>>{}); 
} 

注意,這有出現在各種func_impl重載的聲明。