将重载函数转换为专用函数模板

时间:2017-02-20 15:58:46

标签: c++ templates lambda c++14 overloading

我有一个当前为不同数据类型重载的函数,并使用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>传递给函数,那将是更好的解决方案。

2 个答案:

答案 0 :(得分:5)

您的方法存在一些问题。首先,你不能部分专门化功能模板,所以这是从门口出来的。其次,你通过左值引用来获取你的函数 - 这会阻止你传入lambda,这是一个prvalue。

在这种情况下,很容易在函数模板上添加一些SFINAE,以便只有int&可以调用一个SFINAE,而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
}        

答案 1 :(得分: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

的其他特化,可以轻松扩展