2016-04-18 20 views
6

Tôi biết rằng có nhiều cách có thể để phát hiện nếu một lớp có hàm cụ thể nhưng không thực sự hoạt động cho trường hợp chính xác của tôi. Việc triển khai hiện tại của tôi để kiểm tra chức năng thành viên chính xác có hoạt động không, ngoại trừ các hàm kế thừa.Loại đặc điểm: Kiểm tra xem lớp có hàm cụ thể (có thể kế thừa)

#include <type_traits> 

template<typename T>                 
class HasFoo {                     
    template <typename U, int (U::*)(float)>         
     struct Check;                 

    template <typename U>              
     static std::true_type Test(Check<U, &U::foo> *);     

    template <typename U>               
     static std::false_type Test(...);            

public: 
    static constexpr bool value = decltype(Test<T>(0))::value;      
}; 

struct A { 
    int foo(float); 
}; 

struct B : public A { 
}; 

struct C { 
    unsigned int foo(double); 
}; 

struct D { 
    static int foo(float); 
}; 

static_assert(HasFoo<A>::value, "A should have foo."); 
static_assert(HasFoo<B>::value, "B should inherit foo from A."); 

static_assert(!HasFoo<C>::value, "C should not have foo."); 
static_assert(!HasFoo<D>::value, "Ds static foo should be false."); 

Live example.

thực hiện này không làm việc cho static_assert của B.

Một workaround inacceptable sẽ được kiểm tra:

template <typename U, int (U::A::*)(float)> 
struct Check;     | 
           |- add base class 

Nhưng đó tôi sẽ phải biết lớp cơ sở và điều này nên tránh.

Có ai có ý tưởng về cách kiểm tra các chức năng phái sinh không?

Chỉnh sửa: Đặc điểm kiểu cũng sẽ hoạt động nếu không tồn tại Foo.

struct E {}; 
static_assert(!HasFoo<E>::value, "E does not have foo."); 

Trả lời

3

Dưới đây là một trường học cũ C++ 03 cách để làm việc đó. Thông thường nó có thể được sử dụng như một tiện ích và làm cho nó được đúc cho bất kỳ phương pháp hoặc biến.

#define HasMember(NAME) \ 
    template<class Class, typename Type = void> \ 
    struct HasMember_##NAME \ 
    { \ 
    typedef char (&yes)[2]; \ 
    template<unsigned long> struct exists; \ 
    template<typename V> static yes Check (exists<sizeof(static_cast<Type>(&V::NAME))>*); \ 
    template<typename> static char Check (...); \ 
    static const bool value = (sizeof(Check<Class>(0)) == sizeof(yes)); \ 
    }; \ 
    template<class Class> \ 
    struct HasMember_##NAME<Class, void> \ 
    { \ 
    typedef char (&yes)[2]; \ 
    template<unsigned long> struct exists; \ 
    template<typename V> static yes Check (exists<sizeof(&V::NAME)>*); \ 
    template<typename> static char Check (...); \ 
    static const bool value = (sizeof(Check<Class>(0)) == sizeof(yes)); \ 
    } 

nhanh chóng:

HasMember(Foo); 

Cách sử dụng:

HasMember_Foo<B>::value // without type (but then no overload allowed) 
HasMember_Foo<C, int (C::*)(float)>::value // needs type 

Lưu ý rằng, ở đây tôi đang cung cấp hai HasMember_Foo s, 1 với loại và 1 không có loại. Chúng được tổng quát cho mọi loại (không chỉ riêng cho int (X::*)(float)). Nếu không có kiểu nào được đề cập, thì lớp đó chỉ có 1 phương thức như vậy (không quá tải). Do đó, nó luôn luôn an toàn hơn để đề cập đến loại; Như bạn đã làm trong câu hỏi của mình, loại cụ thể là int (X::*)(float). BTW, điều này cũng có thể được bao gồm bằng cách sử dụng một vĩ mô.
Nếu không có macro bổ sung như vậy, trong trường hợp class Cclass D, bạn có thể phải chỉ định loại phương thức.

Dưới đây là demo bằng mã của bạn.


Ở đây, giả định rằng bất kỳ thành viên nhóm (chức năng hoặc biến) nào được chọn, phải là public phạm vi. tức là nếu X::fooprivate thì giải pháp này sẽ không hoạt động.

3

Dưới đây là một cách để làm điều đó (làm việc cho 4 trường hợp thử nghiệm của bạn, không kiểm tra nó mạnh mẽ mặc dù), nhờ @ Jarod42 để cải thiện (xem câu trả lời ban đầu ở cuối):

template <typename T> 
int call_foo (int (T::*)(float)); 

template <typename C> 
std::true_type has_foo(decltype(call_foo(&C::foo))); 

template <typename C> 
std::false_type has_foo (...);  

template<typename T> 
using HasFoo = decltype(has_foo<T>(0)); 

Sự cố với mã của bạn là bạn mong đợi U::* trong khi &B::fooA::* (không phải B::*). Ở đây tôi cho trình biên dịch chọn giá trị của T bằng cách sử dụng khấu trừ loại ngầm định vì vậy tôi không chạy vào vấn đề như vậy.

Mã này hoạt động như sau:

  • Nếu T không có thành viên foo, sau đó trình biên dịch sẽ chọn sự quá tải thứ hai của has_foo.
  • Nếu T có thành viên foo, trình biên dịch sẽ thử quá tải đầu tiên nhưng sẽ không thành công do không có hàm call_foo phù hợp để nó sẽ chọn lại lần thứ hai và tạo std::false_type.

Mã làm việc trên ideone: http://ideone.com/erh93I.

Bạn có thể đặt tất cả mọi thứ trong một class nếu bạn muốn:

template <typename T> 
class HasFoo { 

    template <typename C> 
    static int call_foo (int (C::*)(float)); 

    template <typename C> 
    static std::true_type has_foo (decltype(call_foo(&C::foo))); 

    template <typename C> 
    static std::false_type has_foo (...); 

public: 
    static constexpr bool value = decltype(has_foo<T>(0)){}; 
}; 
+0

Cảm ơn bạn đã trả lời. Tôi quên đề cập đến: Nếu lớp học không có foo ở tất cả, nó phải biên dịch. Chỉnh sửa câu hỏi của tôi. – Viatorus

+0

@Viatorus Tôi đã cập nhật câu trả lời của tôi - Đây không phải là sạch hơn như phiên bản đầu tiên nhưng điều này có thể được cải thiện. – Holt

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