2017-02-20 24 views
5

Tôi có một chức năng hiện đang bị quá tải cho các kiểu dữ liệu khác nhau và cần một con trỏ hàm (lambda) để khởi tạo các kiểu dữ liệu đó. Tôi đang trong quá trình chuyển đổi chúng thành các bản mẫu nhưng chưa thành công.Chuyển đổi các chức năng quá tải thành các mẫu chức năng chuyên ngành

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

Lưu ý rằng tôi đã lấy ví dụ về intdouble cho đơn giản, họ có thể là một số loại hoàn toàn khác nhau (và phức tạp) trong mã thực tế.


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

Cũng trong khi gọi chức năng templated, nếu tôi không phải vượt qua <any type hints> đến chức năng, đó sẽ là giải pháp tốt hơn nhiều.

+1

'template <> trống doSome (F & func) 'là sai, và thậm chí nếu bạn đã viết mẫu trống doSome (F & func)', bạn không thể chuyên một phần chức năng' mẫu – xinaiz

+0

Tôi chỉ tò mò, tại sao bạn muốn chuyển đổi từ quá tải, thành chuyên môn hóa mẫu? Quá tải thường đẹp hơn và ít gây ngạc nhiên hơn các chuyên môn về mẫu chức năng. Quá tải là lý do tại sao bạn không cần chuyên môn hóa một phần cho các mẫu chức năng. Lời khuyên của tôi cho ai đó sẽ là tránh các mẫu chức năng chuyên biệt bất cứ khi nào có thể. Có thể đây là vấn đề XY không? –

+1

@NirFriedman chức năng con trỏ chỉ làm việc cho lambdas không bắt, và đó là rất hạn chế. 'std :: function' sẽ giải quyết vấn đề này và tôi đã sử dụng nó ở mọi nơi khác nhưng các chức năng này là hiệu năng quan trọng và điểm chuẩn đã cho biết con trỏ hàm nhanh hơn rất nhiều trong trường hợp của chúng tôi, vì vậy .. –

Trả lời

5

Có một số vấn đề với cách tiếp cận của bạn. Đầu tiên, bạn không thể chuyên biệt hóa một phần các mẫu chức năng, vì vậy đó là từ cổng. Thứ hai, bạn đang dùng hàm của bạn bằng tham chiếu lvalue - điều này ngăn cản bạn đi qua một lambda, đó là một giá trị.


Trong trường hợp này, thật dễ dàng để chỉ cần thêm một số SFINAE trên mẫu chức năng của bạn để người duy nhất tham gia giải quyết tình trạng quá tải nếu nó có thể được gọi với int& và người kia chỉ với 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

bạn có thể giải thích một chút về' - > decltype (f (std :: declval ()), void()) '? Làm thế nào nó làm việc ở đây? Không phải '->' có nghĩa là biểu thức giá trị trả về phía trước, và cả hai phương thức đều có int là giá trị trả về và cái gì là 'void()' đang làm ở đây! –

+0

@AbhinavGauniyal [Biểu thức SFINAE] (http: // stackoverflow.com/q/12654067/2069064) – Barry

+0

Tôi đọc câu trả lời đó và hiểu ý chính của nó. Tuy nhiên tôi không thể nắm bắt được những gì 'void()' làm ở đó vì [decltype] (http://en.cppreference.com/w/cpp/language/decltype) không có hai tham số, hay là cú pháp thực hiện Biểu thức SFINAE? –

1

Nếu bạn muốn tạo một phiên bản chung của doSome(), không sử dụng SFINAE để giải quyết quá tải, nó sẽ phức tạp hơn một chút.

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

Cắm mã này vào mã của bạn ... and it works.


Lưu ý rằng tôi không chắc chắn nếu điều này sẽ làm việc với tất cả lambda được viết và hiện không hoạt động với tham chiếu đến hàm. Tuy nhiên, thật dễ dàng để mở rộng, bằng cách thêm các chuyên môn bổ sung của isolate_types.

Các vấn đề liên quan