2012-04-26 37 views
5

Gặp sự cố với SFINAE. Tôi cần để có thể xác định xem một loại có một toán tử hàm thành viên-> được định nghĩa không phụ thuộc vào kiểu trả về của nó. Ví dụ sau.SFINAE - Cố gắng xác định xem loại mẫu có chức năng thành viên có kiểu trả về 'biến' không

Lớp này trong trình kiểm tra. Nó định nghĩa toán tử ->() với kiểu trả về X *. Do đó tôi sẽ không biết 'X' là gì để mã hóa nó ở mọi nơi.

template <class X> 
class PointerX 
{ 
    ... 

    X* operator->() const; 
    ... 
} 

Lớp này cố gắng xác định xem thông tin T có được toán tử phương thức-> xác định hay không; bất kể những gì operator-> return type là.

template<typename T> 
struct HasOperatorMemberAccessor 
{ 
    template <typename R, typename C> static R GetReturnType(R (C::*)()const); 

    template<typename U, typename R, R(U::*)()const> struct SFINAE{}; 
    template<typename U> static char Test(SFINAE<U,  decltype(GetReturnType(&U::operator->)), &U::operator-> >*); 
    template<typename U> static uint Test(...); 
    static const bool value = sizeof(Test<T>(0)) == sizeof(char); 
}; 

Lớp này giống hệt như trên trừ toán tử-> kiểu trả về phải là 'Đối tượng'.

template<typename T> 
struct HasOperatorMemberAccessorOBJECT 
{ 
    template <typename R, typename C> static R GetReturnType(R (C::*)()const); 

    template<typename U, typename R, R(U::*)()const> struct SFINAE{}; 
    template<typename U> static char Test(SFINAE<U,  Object*,    &U::operator-> >*); // only change is we hardcoded Object as return type. 
    template<typename U> static uint Test(...); 
    static const bool value = sizeof(Test<T>(0)) == sizeof(char); 
}; 

Kết quả:

void main() 
{ 
    HasOperatorMemberAccessor<PointerX<Object>>::Test<PointerX<Object>>(0);   // fails ::value is false; Test => Test(...) 

    HasOperatorMemberAccessorOBJECT<PointerX<Object>>::Test<PointerX<Object>>(0);  // works! ::value is true; Test => Test(SFINAE<>*) 
} 

HasOperatorMemberAccessor đã không thể tìm thấy chức năng thành viên PointX của "đối tượng điều hành ->() const". Vì vậy, nó sử dụng thử nghiệm phiên bản chung của Test (...).

Tuy nhiên, HasOperatorMemberAccessorOBJECT có thể tìm thấy "Object operator ->() const" của PointX. Vì vậy, nó sử dụng Kiểm tra phiên bản thử nghiệm chuyên biệt (SFINAE *).

Cả hai nên đã có thể tìm thấy phương thức "Toán tử đối tượng ->() const"; và do đó cả hai nên sử dụng Kiểm tra phiên bản chuyên môn của Test (SFINAE *); và do đó HasOperatorMemberAccessor> :: giá trị phải đúng cho cả hai.

Sự khác biệt duy nhất giữa HasOperatorMemberAccessor và HasOperatorMemberAccessorOBJECT là HasOperatorMemberAccessorOBJECT có typename R hardcoded phản đối,

Vì vậy, vấn đề là "decltype (GetReturnType (& U :: nhà khai thác>))" không được trở về Object đúng. Tôi đã thử một số giấy phép khác nhau để khám phá kiểu trả về. Chúng như sau:

decltype(GetReturnType(&U::operator->)) 
    typename decltype(GetReturnType(&U::operator->)) 
    decltype(((U*)nullptr)->operator->()) 
    typename decltype(((U*)nullptr)->operator->()) 

Không có tác dụng, tại sao? Tôi đang sử dụng MSVC++ 10.0.

+0

Một điều khiến mắt bị đánh trúng là 'PointerX :: operator->' trả về 'bool *', không phải 'bool'. –

+0

Các loại X trong PointerX không quan trọng như xa như HasOperatorMemberAccessor là có liên quan. Tôi đã cố gắng khái quát vấn đề của mình bằng cách không thêm vào nhiều đối tượng không liên quan vào ví dụ. Nếu nó quá khó hiểu, tôi sẽ thay đổi bool thành chuỗi. –

+0

Hãy để tôi thử lại.'PointerX :: toán tử-> 'trả về' Đối tượng * '. 'decltype (...)' sẽ là 'Object *'. 'HasOperatorMemberAccessorOBJECT' dường như thành công, mặc dù nó thay thế' decltype (...) 'bằng' Object'. Có vẻ như có gì đó không ổn ở đây. Tôi không nói về 'HasOperatorMemberAccessor' chút nào. –

Trả lời

4

Bạn đang hỏi cách triển khai một đặc điểm như vậy hay tại sao decltype không hoạt động như bạn mong đợi? Nếu trước đây, đây là một cách tiếp cận:

#include <type_traits> 

template<typename T, bool DisableB = std::is_fundamental<T>::value> 
struct HasOperatorMemberAccessor 
{ 
private: 
    typedef char no; 
    struct yes { no m[2]; }; 

    struct ambiguator { char* operator ->() { return nullptr; } }; 
    struct combined : T, ambiguator { }; 
    static combined* make(); 

    template<typename U, U> struct check_impl; 
    template<typename U> 
    static no check(
     U*, 
     check_impl<char* (ambiguator::*)(), &U::operator ->>* = nullptr 
    ); 
    static yes check(...); 

public: 
    static bool const value=std::is_same<decltype(check(make())), yes>::value; 
}; 

// false for fundamental types, else the definition of combined will fail 
template<typename T> 
struct HasOperatorMemberAccessor<T, true> : std::false_type { }; 

// true for non-void pointers 
template<typename T> 
struct HasOperatorMemberAccessor<T*, false> : 
    std::integral_constant< 
     bool, 
     !std::is_same<typename std::remove_cv<T>::type, void>::value 
    > 
{ }; 

template<typename X> 
struct PointerX 
{ 
    X* operator ->() const { return nullptr; } 
}; 

struct X { }; 

int main() 
{ 
    static_assert(
     HasOperatorMemberAccessor<PointerX<bool>>::value, 
     "PointerX<> has operator->" 
    ); 
    static_assert(
     !HasOperatorMemberAccessor<X>::value, 
     "X has no operator->" 
    ); 
    static_assert(
     HasOperatorMemberAccessor<int*>::value, 
     "int* is dereferencable" 
    ); 
    static_assert(
     !HasOperatorMemberAccessor<int>::value, 
     "int is not dereferencable" 
    ); 
    static_assert(
     !HasOperatorMemberAccessor<void*>::value, 
     "void* is not dereferencable" 
    ); 
} 

VC++ 2010 thiếu cần thiết C++ 11 cơ sở vật chất (ví dụ biểu hiện SFINAE) cần thiết để làm cho điều này sạch hơn nhiều.

+0

Vâng, điều này khá nhiều làm việc cho tôi ngoại trừ việc nó không cho phép T là một loại con trỏ.Tôi đã thay đổi cơ sở thành: cơ sở cấu trúc: có điều kiện :: giá trị, T, không> :: type, base_mixin {}; Điều đó nói rằng, tôi rất muốn biết tại sao decltype không hoạt động. –

+0

@MichaelG: 'HasOperatorMemberAccessor <>' có thể được tầm thường chuyên biệt cho con trỏ: 'mẫu lớp HasOperatorMemberAccessor : public std :: true_type {};'. Đối với 'decltype' không hoạt động như bạn mong đợi, tôi đã không điều tra mã của bạn nhiều, nhưng việc thực thi' decltype' của VC++ 2010 là chuẩn và lỗi trên đầu trang của nó. – ildjarn

+0

@Michael: Đã chỉnh sửa với phát hiện kiểu con trỏ thích hợp ('void *' là một hình ảnh xác thực). – ildjarn

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