2017-02-20 6 views
5

У меня есть функция, которая в настоящее время перегружена для разных типов данных и принимает лямбда (указатель функции) для инициализации этих типов данных. Я собираюсь преобразовать их в экземпляры шаблонов, но пока не удалось.Преобразование перегруженных функций в специализированные шаблоны функций

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; 
} 

Обратите внимание, что я взял пример из int и double для упрощения, они могут быть некоторые совершенно разные (и сложные) типы в настоящем коде.


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 <имяТип F, Int> (F & FUNC)' неправильно, и даже если вы написали 'шаблон недействительной doSome (F & Func)', вы не можете частично специализировать функцию templates – xinaiz

+0

Мне просто интересно, почему вы хотите конвертировать из перегрузки, в специализированную специализацию? Перегрузка обычно лучше и менее неожиданна, чем специализированные шаблоны. Перегрузка - причина, по которой вам не требуется частичная специализация для шаблонов функций. Мой совет кому-то будет заключаться в том, чтобы избежать специализированных шаблонов функций, где это возможно. Возможно ли, что это проблема XY? –

+1

Указатели функции @NirFriedman работают только для не-захвата лямбда, и это очень ограничивает. 'std :: function' решил бы это, и я использовал его везде, но эти функции критичны по производительности, а эталонные указатели указателей функций будут намного быстрее в нашем случае, поэтому .. –

ответ

5

Есть несколько проблем с вашим подходом. Во-первых, вы не можете частично специализировать шаблоны функций, так что это вне ворот. Во-вторых, вы берете свою функцию с помощью ссылки lvalue, которая мешает вам проходить в лямбда, что является 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 
}   
+0

вы можете немного рассказать о' > decltype (f (std :: declval ()), void()) '? Как это работает здесь? Не '' -> 'означает выражение возвращаемого значения вперед, и оба метода имеют int как возвращаемое значение и что это' 'void()' делает здесь! –

+0

@AbhinavGauniyal [Выражение SFINAE] (http: // stackoverflow.com/q/12654067/2069064) – Barry

+0

Я прочитал этот ответ и понял суть его. Тем не менее я не мог понять, что это такое 'void()' делает с тех пор, как [decltype] (http://en.cppreference.com/w/cpp/language/decltype) не принимает два параметра, или это синтаксис реализации Expression 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.


Обратите внимание, что я не уверен, если это будет работать со всей лямбдой, как написано, и что в настоящее время он не будет работать со ссылками на функцию. Однако довольно легко расширить, добавив дополнительные специализации isolate_types.

Смежные вопросы