Bạn có thể thêm một dự phòng trong trường hợp &E::foo
thất bại sử dụng SFINAE (và một số khác trong trường hợp E::foo
không tồn tại ở tất cả):
template <typename T>
std::is_member_pointer<decltype(&T::foo)> is_member_foo(int);
template <typename T>
decltype(T::foo, std::true_type{}) is_member_foo(long);
template <typename T>
std::false_type is_member_foo(...);
template <typename T>
using IsMemberFoo = decltype(is_member_foo<T>(0));
static_assert(IsMemberFoo<A>{}, "No");
static_assert(IsMemberFoo<B>{}, "No");
static_assert(!IsMemberFoo<C>{}, "No");
static_assert(!IsMemberFoo<D>{}, "No");
static_assert(IsMemberFoo<E>{}, "No");
static_assert(!IsMemberFoo<F>{}, "No");
static_assert(!IsMemberFoo<G>{}, "No"); // struct G { };
gì mã này không:
- Nếu
&T::foo
là hợp lệ, nó sẽ kiểm tra xem thành viên có tĩnh hay không bằng cách sử dụng std::is_member_pointer
(phiên bản của bạn).
- Nếu
&T::foo
là không hợp lệ, nó rơi trở lại tình trạng quá tải thứ hai (ở đây bạn chắc chắn rằng foo
không phải là tĩnh, hoặc quá tải đầu tiên sẽ được chọn):
- Nếu
T::foo
là hợp lệ (thành viên tồn tại), nó trả về std::true_type
.
- Otherwize nó rơi trở lại quá tải cuối cùng và trả về
std::false_type
.
Cũng lưu ý (nhờ @iammilind) mà cho private
thành viên, T::foo
là không hợp lệ, vì vậy tình trạng quá tải thứ ba sẽ được chọn.
dụ làm việc trên ideone: http://ideone.com/FILHbK
ghi chú Side (giải thích mở rộng):
- Khi
&T::foo
là hợp lệ, hai định nghĩa chồng đầu tiên là hợp lệ, nhưng một trong những đầu tiên được chọn từ int
là chính xác phù hợp trong khi long
thì không.
decltype(T::foo, std::true_type{})
: T::foo
chỉ ở đây để "để SFINAE" rơi trở lại tình trạng quá tải thứ ba nếu T::foo
không hợp lệ, nhưng loại kết quả là std::true_type
nhờ toán tử dấu phẩy.
Nếu bạn thích, bạn cũng có thể tạo ra một phiên bản generic (http://ideone.com/lzH2FB):
#define IsMember(MEM) \
template <typename T> \
std::is_member_pointer<decltype(&T::MEM)> is_member_##MEM(int); \
template<typename T> \
decltype(T::MEM, std::true_type{}) is_member_##MEM(long); \
template <typename T> \
std::false_type is_member_##MEM(...); \
template <typename T> \
using IsMember_##MEM = decltype(is_member_##MEM<T>(0));
// Instanciate IsMember_foo
IsMember(foo);
// Use it:
static_assert(IsMember_foo<A>{}, "No");
Cũng xem hai câu trả lời này nếu bạn muốn để đóng gói tất cả mọi thứ trong một lớp học (mà không cần phải is_member_
chức năng) :
Related: http://stackoverflow.com/questions/8336274/pointer-to-member-that-is-a-reference-illegal – PiotrNycz