2017-02-20 49 views
5

我有一個函數,當前爲不同的數據類型重載,並採用lambda(函數指針)來初始化這些數據類型。我正在將它們轉換爲模板實例,但尚未成功。將重載函數轉換爲專門的函數模板

Here's the overloaded version -

#include <iostream> 
using namespace std; 


void doSome(int (*func)(int &)){ 
    int a; 
    a = 5; 
    int res = func(a); 
    cout << a << "\n"; 
} 


void doSome(int (*func)(double &)){ 
    double a; 
    a = 5.2; 
    int res = func(a); 
    cout << a << "\n"; 
} 


int main() { 
    doSome([](int &a){ 
     a += 2; 
     return 1; 
    }); 

    doSome([](double &a){ 
     a += 2.5; 
     return 1; 
    }); 
    return 0; 
} 

請注意,我爲簡化拍攝的intdouble例如,他們可能是一些完全不同的(和複雜)類型的實際代碼。


Here's what I've tried yet -

#include <iostream> 
using namespace std; 

template <typename F, typename S> 
void doSome(F &func){ 
    S a; 
    auto res = func(a); 
    cout << res << "\n"; 
} 

template<> 
void doSome<typename F, int> (F &func){ 
    int a; 
    a = 5; 
    auto res = func(a); 
    cout << res << "\n"; 
} 

template<> 
void dpSome<typename F, double> (F &func){ 
    double a; 
    a = 5.5 
    auto res = func(a); 
    cout << res << "\n"; 
} 


int main() { 
    doSome([](int &a){ 
     a += 2; 
     return 1; 
    }); 

    doSome([](double &a){ 
     a += 2.5; 
     return 1; 
    }); 
    return 0; 
} 

而且在調用模板的功能,如果我沒有通過<any type hints>的功能,這將是更好的解決方案。

+1

'模板<> 無效doSome <類型名樓int>的(F&FUNC)'是錯誤的,即使你寫'模板 無效doSome (F&FUNC)',你不能部分專門功能模板 – xinaiz

+0

我只是好奇,爲什麼你想從重載轉換爲模板專業化?重載通常比函數模板專業化更好,並不令人驚訝。重載是你不需要對函數模板進行部分特化的原因。我對某人的建議是儘可能避免專用功能模板。這可能是一個XY問題嗎? –

+1

@NirFriedman函數指針只適用於非捕獲lambda表達式,這是非常有限的。 'std :: function'會解決這個問題,我在其他地方使用過,但是這些函數對性能至關重要,基準測試表明函數指針在我們的例子中要快得多,所以.. –

回答

5

您的方法有幾個問題。首先,你不能部分地專注於功能模板,所以這就離開了門戶。其次,你正在通過左值引用來引用你的函數 - 這會阻止你傳入一個lambda,這是一個前值。


在這種情況下,很容易只需添加一些SFINAE你的函數模板,這樣一個只在重載參與,如果能與int&被調用,而其他只與double&

template <class F> 
auto doSome(F f) 
    -> decltype(f(std::declval<int&>()), void()) 
{ 
    // int& case 
}   

template <class F> 
auto doSome(F f) 
    -> decltype(f(std::declval<double&>()), void()) 
{ 
    // double& case 
}   
+0

你能解釋一下關於' > decltype(f(std :: declval ()),void())'?它在這裏如何工作? ' - >'表示前面的返回值表達式,並且這兩個方法都有int作爲返回值,那麼'void()'在這裏做什麼! –

+0

@AbhinavGauniyal [Expression SFINAE](http:// stackoverflow。com/q/12654067/2069064) – Barry

+0

我讀到了答案並理解了它的要點。我仍然無法理解'void()'在那裏做什麼,因爲[decltype](http://en.cppreference.com/w/cpp/language/decltype)不需要兩個參數,或者它是語法實現表達式SFINAE? –

1

如果要製作通用版本的doSome(),它不使用SFINAE進行重載分辨率,則會變得更復雜一些。

#include <type_traits> // For std::remove_reference_t. 

namespace detail { 
    // Helper to isolate return and parameter types, for a single-parameter callable. 
    template<typename T> 
    struct isolate_types; 

    // Function. 
    template<typename R, typename P> 
    struct isolate_types<R(P)>    { using Ret = R; using Param = P; }; 

    // Function pointer. 
    template<typename R, typename P> 
    struct isolate_types<R(*)(P)>   { using Ret = R; using Param = P; } 

    // Pointer-to-member-function. Used for lambdas & functors. 
    // Assumes const this pointer. 
    template<typename R, typename C, typename P> 
    struct isolate_types<R (C::*)(P) const> { using Ret = R; using Param = P; }; 

    // Lambda. Uses lambda's operator(). 
    // Credit goes to ecatmur: http://stackoverflow.com/a/13359520/5386374 
    template<typename T> 
    struct isolate_types : isolate_types<decltype(&std::remove_reference_t<T>::operator())> {}; 

    // Individual type aliases. 
    template<typename T> 
    using IsolateReturn = typename isolate_types<T>::Ret; 
    template<typename T> 
    using IsolateParam = typename isolate_types<T>::Param; 

    // Internal values, used by doSome(). 
    template<typename T> T value; 

    template<> constexpr int value<int> = 5; 
    template<> constexpr double value<double> = 5.2; 
    // Define others as needed... 
} // namespace detail 

template<typename F> 
void doSome(F func) { 
    // Determine necessary types. 
    using Ret = detail::IsolateReturn<F>; 
    using Param = std::remove_reference_t<detail::IsolateParam<F>>; 

    // And voila. 
    Param a = detail::value<Param>; 
    Ret res = func(a); // Can also use auto, if Ret isn't needed elsewhere. 
    std::cout << a << "\n"; 
} 

將此代碼插入代碼... and it works


請注意,我不知道這是否會與所有工作lambda表達式書面,而且它目前不會對函數的引用工作。然而,通過增加isolate_types的額外專業化,擴展很容易。