2017-02-20 52 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的额外专业化,扩展很容易。