2012-01-31 45 views
12

Làm cách nào tôi có thể suy luận tĩnh nếu đối số là đối tượng hàm C++ (functor)?Có phải lớp đặc điểm is_functor C++ có thể không?

template <typename F> 
void test(F f) {} 

Tôi đã thử is_function<F>::value, nhưng cách này không hiệu quả. Dường như không có đặc tính is_functor, vì vậy có lẽ điều đó là không thể. Tôi dường như chỉ tìm kiếm một hàm thành viên cụ thể, trong trường hợp này là toán tử gọi hàm: F::operator().

+0

làm thế nào về 'is_function :: value'? – Fiktik

+1

http://groups.google.com/group/comp.lang.c++.moderated/msg/e5fbc9305539f699 có thể bạn quan tâm. – pmr

+1

Bạn chỉ muốn thử nghiệm cho các functors hoặc cho bất kỳ đối tượng có thể gọi? Dường như một số tính năng 'result_of' của SFINAE sẽ hoạt động để xác định bất kỳ loại có thể gọi nào. Tôi hơi ngạc nhiên rằng dường như không có bất kỳ đặc điểm 'std :: is_callable' nào. – bames53

Trả lời

0
template<typename T, typename Sign>         
struct is_functor 
{                 
    typedef char yes[1];            
    typedef char no [2];            
    template <typename U, U> struct type_check;      
    template <typename _1> static yes &chk(type_check<Sign, &_1::operator()>*); 
    template <typename > static no &chk(...);      
    static bool const value = sizeof(chk<T>(nullptr)) == sizeof(yes);  
}; 

Thay đổi từ this answer.

Nó có thể được sử dụng như ...

template<typename T> 
typename std::enable_if<is_functor<T, void(T::*)()>::value>::type func() 
{ 
} 
+0

'typename decltype'? Và giải pháp của bạn không hoạt động nếu toán tử '()' bị quá tải. – kennytm

+0

Định nghĩa của bạn về functor chưa hoàn thành. Một hàm functor tiêu chuẩn là một con trỏ hàm hoặc một đối tượng với toán tử 'quá tải()' quá tải. –

+0

Tôi đã đăng một giải pháp khác; @MaximYegorushkin nhưng cái mới không thay đổi về điều đó, hmmm – David

12

Nó có thể tạo ra một đặc điểm như vậy, với hai hạn chế:

  1. Đối với trình biên dịch, một chức năng miễn phí là một cái gì đó hoàn toàn khác nhau từ một hàm lớp quá tải operator(). Vì vậy, chúng ta phải đối xử với cả hai trường hợp một cách riêng biệt khi thực hiện. Tuy nhiên, đây không phải là vấn đề khi sử dụng, chúng tôi có thể ẩn chi tiết triển khai này từ người dùng.
  2. Chúng tôi cần biết chữ ký của hàm bạn muốn gọi. Điều này thường không phải là một vấn đề, và nó có tác dụng phụ tốt đẹp mà đặc điểm của chúng tôi có thể xử lý tình trạng quá tải khá nguyên bản.

Bước một: miễn phí chức năng

Hãy bắt đầu với chức năng miễn phí, bởi vì họ dễ dàng hơn để phát hiện. Nhiệm vụ của chúng tôi là, khi được đưa ra một con trỏ hàm, để xác định xem chữ ký của con trỏ hàm đó có khớp với chữ ký được chuyển làm đối số mẫu thứ hai hay không. Để có thể so sánh chúng, chúng ta cần nắm bắt chữ ký hàm cơ bản, hoặc tạo một con trỏ hàm của chữ ký của chúng ta. Tôi tùy tiện chọn sau:

// build R (*)(Args...) from R (Args...) 
// compile error if signature is not a valid function signature 
template <typename, typename> 
struct build_free_function; 

template <typename F, typename R, typename ... Args> 
struct build_free_function<F, R (Args...)> 
{ using type = R (*)(Args...); }; 

Bây giờ tất cả những gì còn lại để làm là để so sánh và chúng tôi đang thực hiện với các chức năng phần miễn phí:

// determine whether a free function pointer F has signature S 
template <typename F, typename S> 
struct is_function_with_signature 
{ 
    // check whether F and the function pointer of S are of the same 
    // type 
    static bool constexpr value = std::is_same< 
     F, typename build_free_function<F, S>::type 
    >::value; 
}; 

Bước hai: functors Lớp

Cái này có liên quan nhiều hơn một chút. Chúng ta có thể dễ dàng phát hiện với SFINAE dù một lớp định nghĩa một operator():

template <typename T> 
struct defines_functor_operator 
{ 
    typedef char (& yes)[1]; 
    typedef char (& no)[2]; 

    // we need a template here to enable SFINAE 
    template <typename U> 
    static yes deduce(char (*)[sizeof(&U::operator())]); 
    // fallback 
    template <typename> static no deduce(...); 

    static bool constexpr value = sizeof(deduce<T>(0)) == sizeof(yes); 
}; 

nhưng điều đó không cho chúng tôi biết một tồn tại cho chữ ký chức năng mong muốn của chúng tôi! May mắn thay, chúng ta có thể sử dụng một thủ thuật ở đây: con trỏ là các tham số mẫu hợp lệ. Vì vậy trước tiên chúng ta có thể sử dụng viên con trỏ hàm của chữ ký mong muốn của chúng tôi, và kiểm tra xem &T::operator() là loại rằng:

template <typename T, T> struct check; 

Bây giờ check<void (C::*)() const, &C::operator()> sẽ chỉ có một mẫu instantiation hợp lệ nếu C không thực sự có một void C::operator()() const. Nhưng để làm điều này trước tiên chúng ta phải kết hợp C và chữ ký cho con trỏ hàm thành viên. Như chúng ta đã thấy, chúng ta cần phải lo lắng về hai trường hợp thêm chúng tôi không phải quan tâm đến các chức năng miễn phí: constvolatile chức năng.Bên cạnh đó nó khá nhiều giống nhau:

// build R (C::*)(Args...) from R (Args...) 
//  R (C::*)(Args...) const from R (Args...) const 
//  R (C::*)(Args...) volatile from R (Args...) volatile 
// compile error if signature is not a valid member function signature 
template <typename, typename> 
struct build_class_function; 

template <typename C, typename R, typename ... Args> 
struct build_class_function<C, R (Args...)> 
{ using type = R (C::*)(Args...); }; 

template <typename C, typename R, typename ... Args> 
struct build_class_function<C, R (Args...) const> 
{ using type = R (C::*)(Args...) const; }; 

template <typename C, typename R, typename ... Args> 
struct build_class_function<C, R (Args...) volatile> 
{ using type = R (C::*)(Args...) volatile; }; 

Đưa đó và phát hiện của chúng tôi liên quan đến struct check helper với nhau, chúng tôi nhận metafunction kiểm tra của chúng tôi cho các đối tượng functor:

// determine whether a class C has an operator() with signature S 
template <typename C, typename S> 
struct is_functor_with_signature 
{ 
    typedef char (& yes)[1]; 
    typedef char (& no)[2]; 

    // helper struct to determine that C::operator() does indeed have 
    // the desired signature; &C::operator() is only of type 
    // R (C::*)(Args...) if this is true 
    template <typename T, T> struct check; 

    // T is needed to enable SFINAE 
    template <typename T> static yes deduce(check< 
     typename build_class_function<C, S>::type, &T::operator()> *); 
    // fallback if check helper could not be built 
    template <typename> static no deduce(...); 

    static bool constexpr value = sizeof(deduce<C>(0)) == sizeof(yes); 
}; 

Bước ba: Đưa các mảnh cùng nhau

Chúng tôi sắp hoàn tất. Bây giờ chúng ta chỉ cần quyết định khi nào sử dụng chức năng miễn phí của chúng ta, và khi các hàm functor của lớp. May mắn thay, C++ 11 cung cấp cho chúng tôi một đặc điểm std::is_class mà chúng tôi có thể sử dụng cho việc này. Vì vậy, tất cả chúng ta phải làm là chuyên về một tham số boolean:

// C is a class, delegate to is_functor_with_signature 
template <typename C, typename S, bool> 
struct is_callable_impl 
    : std::integral_constant< 
     bool, is_functor_with_signature<C, S>::value 
     > 
{}; 

// F is not a class, delegate to is_function_with_signature 
template <typename F, typename S> 
struct is_callable_impl<F, S, false> 
    : std::integral_constant< 
     bool, is_function_with_signature<F, S>::value 
     > 
{}; 

Vì vậy, chúng tôi cuối cùng có thể thêm đoạn cuối cùng của câu đố, là thực tế is_callable đặc điểm của chúng tôi:

// Determine whether type Callable is callable with signature Signature. 
// Compliant with functors, i.e. classes that declare operator(); and free 
// function pointers: R (*)(Args...), but not R (Args...)! 
template <typename Callable, typename Signature> 
struct is_callable 
    : is_callable_impl< 
     Callable, Signature, 
     std::is_class<Callable>::value 
     > 
{}; 

Bây giờ chúng ta dọn dẹp của chúng tôi mã, đặt chi tiết triển khai vào các không gian tên ẩn danh để chúng không có khả năng bên ngoài tệp của chúng tôi và có một số is_callable.hpp để sử dụng trong dự án của chúng tôi.

Full đang

namespace // implementation detail 
{ 
    // build R (*)(Args...) from R (Args...) 
    // compile error if signature is not a valid function signature 
    template <typename, typename> 
    struct build_free_function; 

    template <typename F, typename R, typename ... Args> 
    struct build_free_function<F, R (Args...)> 
    { using type = R (*)(Args...); }; 

    // build R (C::*)(Args...) from R (Args...) 
    //  R (C::*)(Args...) const from R (Args...) const 
    //  R (C::*)(Args...) volatile from R (Args...) volatile 
    // compile error if signature is not a valid member function signature 
    template <typename, typename> 
    struct build_class_function; 

    template <typename C, typename R, typename ... Args> 
    struct build_class_function<C, R (Args...)> 
    { using type = R (C::*)(Args...); }; 

    template <typename C, typename R, typename ... Args> 
    struct build_class_function<C, R (Args...) const> 
    { using type = R (C::*)(Args...) const; }; 

    template <typename C, typename R, typename ... Args> 
    struct build_class_function<C, R (Args...) volatile> 
    { using type = R (C::*)(Args...) volatile; }; 

    // determine whether a class C has an operator() with signature S 
    template <typename C, typename S> 
    struct is_functor_with_signature 
    { 
     typedef char (& yes)[1]; 
     typedef char (& no)[2]; 

     // helper struct to determine that C::operator() does indeed have 
     // the desired signature; &C::operator() is only of type 
     // R (C::*)(Args...) if this is true 
     template <typename T, T> struct check; 

     // T is needed to enable SFINAE 
     template <typename T> static yes deduce(check< 
      typename build_class_function<C, S>::type, &T::operator()> *); 
     // fallback if check helper could not be built 
     template <typename> static no deduce(...); 

     static bool constexpr value = sizeof(deduce<C>(0)) == sizeof(yes); 
    }; 

    // determine whether a free function pointer F has signature S 
    template <typename F, typename S> 
    struct is_function_with_signature 
    { 
     // check whether F and the function pointer of S are of the same 
     // type 
     static bool constexpr value = std::is_same< 
      F, typename build_free_function<F, S>::type 
     >::value; 
    }; 

    // C is a class, delegate to is_functor_with_signature 
    template <typename C, typename S, bool> 
    struct is_callable_impl 
     : std::integral_constant< 
      bool, is_functor_with_signature<C, S>::value 
      > 
    {}; 

    // F is not a class, delegate to is_function_with_signature 
    template <typename F, typename S> 
    struct is_callable_impl<F, S, false> 
     : std::integral_constant< 
      bool, is_function_with_signature<F, S>::value 
      > 
    {}; 
} 

// Determine whether type Callable is callable with signature Signature. 
// Compliant with functors, i.e. classes that declare operator(); and free 
// function pointers: R (*)(Args...), but not R (Args...)! 
template <typename Callable, typename Signature> 
struct is_callable 
    : is_callable_impl< 
     Callable, Signature, 
     std::is_class<Callable>::value 
     > 
{}; 

Ideone ví dụ với một số xét nghiệm

http://ideone.com/7PWdiv

+0

Wow.Wow.Wow.Wow.Wow. – mark

0

Mặc dù điều này không làm việc cho các chức năng quá tải, cho tất cả các trường hợp khác (chức năng miễn phí, các lớp học thực hiện operator() và lambdas) giải pháp ngắn này hoạt động trong C++ 11:

template <typename T, typename Signature> 
struct is_callable: std::is_convertible<T,std::function<Signature>> { }; 

Lưu ý: std::is_callable sẽ có sẵn trong C++ 17.

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