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

Convert overloaded functions to specialized function templates

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

您的方法存在一些问题。首先,您不能部分特化函数模板,所以这是不可能的。其次,您通过左值引用获取函数 - 这会阻止您传入 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
}        

如果您想制作 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: 
    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.

的额外特化,它很容易扩展