2015-12-21 31 views
7

Để phát hiện thành viên trong C++ 14, tôi đã sử dụng mã dựa trên ví dụ here, nhưng dường như không hoạt động.Phát hiện thành viên bằng cách sử dụng void_t

Một ví dụ hoàn chỉnh:

#include <string> 

template <typename...> 
using void_t = void; 

template <typename, typename = void> class HasMember_substr : public std::false_type {}; 
template <typename T> class HasMember_substr<T, void_t<typename T::substr>> : public std::true_type {}; 

template <typename, typename = void> class HasMember_fff : public std::false_type {}; 
template <typename T> class HasMember_fff<T, void_t<typename T::fff>> : public std::true_type {}; 

static_assert(HasMember_substr<std::string>::value, ""); 
static_assert(!HasMember_fff<std::string>::value, ""); 

int main() { return 0; } 

Biên soạn bằng clang++ --std=c++14 test.cpp trên OS X, phiên bản trình biên dịch (clang++ --version): Apple LLVM version 7.0.2 (clang-700.1.81)

Các khẳng định thứ hai thành công, nhưng là người đầu tiên thất bại. Tại sao? Tôi cũng đã thử sử dụng decltype(T::substr) thay vì typename T::subset, với cùng một kết quả.

+2

'T :: substr' không giống với' T {}. Substr' –

+1

Có phải 'substr' là một hàm bị quá tải trong quá trình triển khai của bạn không? (Nó được cho phép.) – aschepler

+1

Bạn đã sao chép một ví dụ về thử nghiệm cho một loại, và mong đợi mã tương tự để làm việc để thử nghiệm cho một chức năng thành viên. Điều đó rõ ràng sẽ không hoạt động. 'std :: string :: substr' không phải là một kiểu, do đó,' typename T :: substr' rõ ràng là vô nghĩa. Bạn nên đã sao chép ví dụ rằng các bài kiểm tra cho pre-increment. –

Trả lời

11

Tìm kiếm T::substr không giống như tìm kiếm hàm thành viên có tên substr. gcc.godbolt.org example

Bạn có thể kiểm tra xem chức năng thành viên có tồn tại không bằng cách sử dụng std::declval<T>() và sử dụng decltype để nhận loại trả về của hàm thành viên.

Nếu chức năng thành viên tồn tại, decltype(...) sẽ là biểu thức đúng ngữ pháp và sẽ không kích hoạt SFINAE - do đó, static_assert sẽ hoạt động chính xác.

#include <string> 
#include <type_traits> 
#include <utility> 

template <typename...> 
using void_t = void; 

template <typename, typename = void> 
class HasMember_substr : public std::false_type {}; 

template <typename T> 
class HasMember_substr<T, void_t< 
    decltype(std::declval<T>().substr(1, 1))> 
> : public std::true_type {}; 

static_assert(HasMember_substr<std::string>::value, ""); 

int main() { return 0; } 

Lưu ý rằng decltype(std::declval<T>().substr(1, 1)) kiểm tra xem T có thành viên substr có thể được gọi với đối số 1, 1. (này không được đảm bảo là một hàm thành viên, nó cũng có thể là một thành viên dữ liệu functor, ví dụ.)


Như đã nói bởi AndyG trong các ý kiến, một cách tiếp cận có thể được sử dụng decltype để "xác thực" loại con trỏ hàm thành viên.

Ví dụ:

HasMember_substr<T, void_t< decltype(&T::substr)> 

Lưu ý rằng điều này sẽ không hoạt động nếu tên substr bị quá tải, và nó is not guaranteed to work with any type in the standard library.

+1

Tôi có thể đơn giản hóa là 'HasMember_substr ' – AndyG

+0

@AndyG: điểm tốt. Thêm giải pháp của bạn vào câu trả lời. –

+5

@AndyG điều này sẽ không hoạt động nếu tên 'substr' bị quá tải –

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