将过载功能转换为专用功能模板

Convert overloaded functions to specialized function templates

本文关键字:功能 专用 转换      更新时间:2023-10-16

我有一个函数,当前对不同的数据类型过载,并采用lambda(功能指针)来初始化这些数据类型。我正在将它们转换为模板实例,但尚未成功。

这是超载版本 -

#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的示例为了简化实际代码中的某些完全不同(和复杂)类型。


这是我尝试的 -

#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>传递给该功能,那将是更好的解决方案。

您的方法有一些问题。首先,您不能部分地专业化功能模板,因此从大门出发。其次,您是通过lvalue参考来掌握您的功能 - 阻止您通过lambda,这是一个prvalue。


在这种情况下,很容易在您的功能模板上添加一些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: 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";
}

将其插入您的代码...


请注意,我不确定这是否适用于书面的所有lambdas,并且目前不适用于对功能的引用。但是,通过添加isolate_types的其他专业。

,它很容易扩展。