2016-03-11 15 views
6

TL; DR Tôi muốn viết hàm mẫu Process(T value) hoạt động khác nhau cho các giá trị khác nhau tùy thuộc vào sự tồn tại của hàm không phải thành viên CreateProcessor<T>(). Tôi có thể làm gì cho điều đó?SFINAE để phát hiện sự tồn tại của hàm mẫu không phải thành viên

Tôi gặp sự cố với SFINAE. Giả sử chúng ta cần hỗ trợ hàm CreateProcessor trả về việc triển khai giao diện IProcessor<T> đối với một số loại loại T.

Trong C++ chúng ta không thể tạo một số quá tải của một hàm chỉ khác nhau về kiểu trả về, vì vậy chúng ta phải thực hiện hàm CreateProcessor cũng là hàm mẫu được parametrized bởi T.

Bây giờ giả sử rằng chúng ta muốn viết một mẫu chức năng Process<T>(T value) rằng công trình khác nhau tùy thuộc vào sự tồn tại của CreateProcessor<T>(), cụ thể là nó nên xử lý value sử dụng bộ vi xử lý trong trường hợp CreateProcessor<T>() được thực hiện, nếu không nó sẽ gây ra lỗi.

Tôi đã cố gắng để viết đoạn mã sau:

#include <cstdio> 
#include <type_traits> 

// A workaround for void_t as described here: http://en.cppreference.com/w/cpp/types/void_t. 
template<typename... Ts> struct make_void { typedef void type;}; 
template<typename... Ts> using void_t = typename make_void<Ts...>::type; 

// An interface for a processor that receives a value of specific type. 
template<class T> 
class IProcessor { 
public: 
    virtual void process(T value) = 0; 
}; 

// A processor for int. 
class IntProcessor : public IProcessor<int> { 
public: 
    virtual void process(int value) override { 
     printf("IntProcessor::process is called for value = %d\n", value); 
    } 
}; 

// Template prototype. 
template<class T> 
IProcessor<T>* CreateProcessor(); 

// Template specialization for int. 
template<> 
IProcessor<int>* CreateProcessor() { 
    return new IntProcessor(); 
} 

// Detector of CreateProcessor. 
template<class, class=void> 
struct CreateProcessorImplemented : std::false_type { }; 

template<class T> 
struct CreateProcessorImplemented<T, void_t<decltype(CreateProcessor<T>())>> : std::true_type { }; 


// Specializations depending on existence of CreateProcessor. 
template <typename T> 
typename std::enable_if<CreateProcessorImplemented<T>::value, void>::type Process(T value) { 
    IProcessor<T>* processor = CreateProcessor<T>(); 
    processor->process(value); 
} 

template <typename T> 
typename std::enable_if<!CreateProcessorImplemented<T>::value, void>::type Process(T value) { 
    printf("Processor for requested typename is unavailable\n"); 
} 


int main() { 
    Process(42); 
    Process("abc"); 

// static_assert(!CreateProcessorImplemented<char const*>::value, ":("); 
/* This static_assert fails with an error: 
* code.cpp:56:5: error: static assertion failed: :(
*  static_assert(!CreateProcessorImplemented<char const*>::value, ":("); 
*/ 
} 

Mặc dù kết quả này do lỗi liên kết:

/tmp/ccTQRc9N.o:code.cpp:function std::enable_if<CreateProcessorImplemented<char const*, void>::value, void>::type Process<char const*>(char const*): error: undefined reference to 'IProcessor<char const*>* CreateProcessor<char const*>()' 
collect2: error: ld returned 1 exit status 

Ý tưởng của tôi là khi chúng ta giải quyết CreateProcessorImplemented<char const*>, decltype(CreateProcessor<const char*>()) không thất bại vì không một mẫu thử nghiệm IProcessor<T> CreateProcessor() và trình biên dịch xem xét các decltype được bằng IProcessor<T> đó là bằng cách nào đó hợp lý nhưng không phải những gì tôi cần.

+0

'CreateProcessorImplemented' luôn xuất phát từ 'std :: true_type' vì một chức năng không cần phải có một thực hiện cho' decltype' để cho bạn biết các kiểu trả về (xem' std :: declval'). – Simple

+0

@ Đơn giản, Điều đó tạo nên sự sence. Tôi tin rằng không có cách nào để kiểm tra nếu chức năng có một thực hiện trong thời gian biên dịch vì nó đòi hỏi một kiến ​​thức từ linker, phải không? –

+0

Giải pháp thay thế nằm trong câu trả lời từ @WojciechFrohmberg. Bạn sử dụng một 'struct' với một hàm thành viên tĩnh thay vì một hàm không phải thành viên. Bạn * có thể * phát hiện nếu không có hàm thành viên tĩnh do 'struct' không được chuyên biệt hóa. Về cơ bản nó là một đặc điểm kiểu. – Simple

Trả lời

5

Một cách để làm cho nó hoạt động là sử dụng wrapper struct hoạt CreateProcessor như thế này:

#include <cstdio> 
#include <type_traits> 

// A workaround for void_t as described here: http://en.cppreference.com/w/cpp/types/void_t. 
template<typename... Ts> struct make_void { typedef void type;}; 
template<typename... Ts> using void_t = typename make_void<Ts...>::type; 

// An interface for a processor that receives a value of specific type. 
template<class T> 
class IProcessor { 
public: 
    virtual void process(T value) = 0; 
}; 

// A processor for int. 
class IntProcessor : public IProcessor<int> { 
public: 
    virtual void process(int value) override { 
     printf("IntProcessor::process is called for value = %d\n", value); 
    } 
}; 

// Template prototype. 
template<class T> 
struct ProcessorCreator: std::false_type { 
    static IProcessor<T>* CreateProcessor(); 
}; 

// Template specialization for int. 
template<> 
struct ProcessorCreator<int>: std::true_type { 
static IProcessor<int>* CreateProcessor() { 
    return new IntProcessor(); 
} 
}; 

// Detector of CreateProcessor. 
template<class, class=void> 
struct CreateProcessorImplemented : std::false_type { }; 

template<class T> 
struct CreateProcessorImplemented<T, typename std::enable_if<ProcessorCreator<T>::value>::type > : std::true_type { }; 


// Specializations depending on existence of CreateProcessor. 
template <typename T> 
typename std::enable_if<CreateProcessorImplemented<T>::value, void>::type Process(T value) { 
    IProcessor<T>* processor = ProcessorCreator<T>::CreateProcessor(); 
    processor->process(value); 
} 

template <typename T> 
typename std::enable_if<!CreateProcessorImplemented<T>::value, void>::type Process(T value) { 
    printf("Processor for requested typename is unavailable\n"); 
} 


int main() { 
    Process(42); 
    Process("abc"); 

// static_assert(!CreateProcessorImplemented<char const*>::value, ":("); 
/* This static_assert fails with an error: 
* code.cpp:56:5: error: static assertion failed: :(
*  static_assert(!CreateProcessorImplemented<char const*>::value, ":("); 
*/ 
} 

Hoặc bạn có thể loại bỏ lời tuyên bố mẫu và vượt qua IProcessor kiểu mẫu tham số sử dụng overloadings chức năng - bằng cách tạo lập luận giả :

#include <cstdio> 
#include <type_traits> 

// A workaround for void_t as described here: http://en.cppreference.com/w/cpp/types/void_t. 
template<typename... Ts> struct make_void { typedef void type;}; 
template<typename... Ts> using void_t = typename make_void<Ts...>::type; 

// An interface for a processor that receives a value of specific type. 
template<class T> 
class IProcessor { 
public: 
    virtual void process(T value) = 0; 
}; 

// A processor for int. 
class IntProcessor : public IProcessor<int> { 
public: 
    virtual void process(int value) override { 
     printf("IntProcessor::process is called for value = %d\n", value); 
    } 
}; 


IProcessor<int>* CreateProcessor(const int&) { 
    return new IntProcessor(); 
} 

// Detector of CreateProcessor. 
template<class, class=void> 
struct CreateProcessorImplemented : std::false_type { }; 

template<class T> 
struct CreateProcessorImplemented<T, void_t<decltype(CreateProcessor(std::declval<T>()))>> : std::true_type { }; 


// Specializations depending on existence of CreateProcessor. 
template <typename T> 
typename std::enable_if<CreateProcessorImplemented<T>::value, void>::type Process(T value) { 
    IProcessor<T>* processor = CreateProcessor(value); 
    processor->process(value); 
} 

template <typename T> 
typename std::enable_if<!CreateProcessorImplemented<T>::value, void>::type Process(T value) { 
    printf("Processor for requested typename is unavailable\n"); 
} 


int main() { 
    Process(42); 
    Process("abc"); 

// static_assert(!CreateProcessorImplemented<char const*>::value, ":("); 
/* This static_assert fails with an error: 
* code.cpp:56:5: error: static assertion failed: :(
*  static_assert(!CreateProcessorImplemented<char const*>::value, ":("); 
*/ 
} 
Các vấn đề liên quan