Nó có thể tạo ra một đặc điểm như vậy, với hai hạn chế:
- Đố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.
- 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í: const
và volatile
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
làm thế nào về 'is_function :: value'? –
Fiktik
http://groups.google.com/group/comp.lang.c++.moderated/msg/e5fbc9305539f699 có thể bạn quan tâm. – pmr
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