2017-02-20 14 views
5

Mam funkcję, która jest obecnie przeciążona dla różnych typów danych i pobiera lambdę (wskaźnik funkcji), aby zainicjować te typy danych. Trwają konwersje na instancje szablonów, ale jeszcze się nie udało.Konwersja przeciążonych funkcji do wyspecjalizowanych szablonów funkcji

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

Zauważ, że Wziąłem przykład z int i double dla uproszczenia, mogą być pewne zupełnie różne (i skomplikowane) typy w rzeczywistym kodzie.


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

Również podczas wywoływania funkcji na matrycy, jeśli nie mam do przekazania <any type hints> do funkcji, które byłyby znacznie lepsze rozwiązanie.

+1

'szablon <> void doSome (F & func)' jest źle, a nawet jeśli masz napisane szablonu void funkcja 'doSome (F & funkcjono)', nie można częściowo specjalizują szablony – xinaiz

+0

Jestem po prostu ciekawy, dlaczego chcesz przekonwertować z przeciążenia, do specjalizacji szablonów? Przeciążanie jest zazwyczaj ładniejsze i mniej zaskakujące niż specjalizacje szablonów funkcji. Przeciążenie jest powodem, dla którego nie potrzebujesz częściowej specjalizacji dla szablonów funkcji. Moja rada dla kogoś polegałaby na tym, aby w miarę możliwości unikać szablonów funkcji. Czy to możliwe, że jest to problem XY? –

+1

Wskaźniki funkcji @NirFriedman działają tylko w przypadku niezapisujących lambd i to bardzo ogranicza. 'std :: function' rozwiązałoby to i używałem go wszędzie, ale te funkcje są krytyczne dla wydajności i testy wskazują, że wskaźniki funkcji są znacznie szybsze w naszym przypadku, więc .. –

Odpowiedz

5

Jest kilka problemów z twoim podejściem. Po pierwsze, nie możesz częściowo wyspecjalizować szablonów funkcji, więc jest to wyjście z bramki. Po drugie, przejmujesz swoją funkcję przez odniesienie do lwartości - co zapobiega przekazywaniu lambda, które jest wartością pryncypium.


W tym przypadku jest to łatwe, aby po prostu dodać trochę SFINAE na szablonie funkcji tak, że jeden bierze udział tylko w rozdzielczości przeciążenia jeśli to można nazwać z int& a drugi tylko z 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

czy możesz wyjaśnić nieco o' - > decltype (f (std :: declval ()), void()) '? Jak to działa? Czy '->' nie oznacza wyrażenia wartości zwracanej naprzód, a obie metody mają int jako wartość zwracaną i co tutaj robi 'void()'! –

+0

@AbhinavGauniyal [Wyrażenie SFINAE] (http: // stackoverflow.com/q/12654067/2069064) – Barry

+0

Przeczytałem tę odpowiedź i zrozumiałem jej sedno. Nadal nie mogłem zrozumieć, co to "void()" robi tam od [decltype] (http://en.cppreference.com/w/cpp/language/decltype) nie bierze dwóch params, czy jest to składnia wdrażania Expression SFINAE? –

1

Jeśli chcesz utworzyć ogólną wersję doSome(), która nie korzysta z SFINAE dla rozdzielczości przeciążenia, staje się nieco bardziej skomplikowana.

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

Podłączenie tego do kodu ... and it works.


Zauważ, że nie jestem pewien, czy to zadziała w przypadku wszystkich lambd jak napisano, i że obecnie nie będzie działać z odniesieniami do funkcji. Rozszerzenie można jednak łatwo rozszerzyć, dodając dodatkowe specjalizacje: isolate_types.

Powiązane problemy