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.
'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
@ Đơ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? –
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