#cpp #cpp11 #шаблоны_с++ #метапрограммирование
Метафункция принимает в шаблоне проверяемый тип. Если оператор скобок определен у Method<данный_тип>, то метафункция должна вернуть true. Иначе false. #include#include template struct Method {}; template<> struct Method { int operator()() { return 1; } }; template struct is_brackets_op_defined { static void Check(...); template static decltype(Method ::operator()) Check(const C&); using type = decltype(Check( std::declval< Method >() )); constexpr static bool value = !std::is_same (); }; int main() { std::cout << is_brackets_op_defined ::value << std::endl; } Моя реализация всегда возвращает false, даже если специализация существует (здесь для int) std::cout << is_brackets_op_defined ::value // false std::cout << is_brackets_op_defined ::value // false
Ответы
Ответ 1
Отлаживать метапрограммы не самое простое знятие, но мы все же попробуем. Для начала уберем использование is_brackets_op_definedи объявление static void Check(...);. Это поможет нам увидеь чем-же компилятору не понравилась перегруженная версия Check(код). main.cpp:19:30: error: no matching function for call to 'is_brackets_op_defined ::Check(Method )' main.cpp:17:42: note: candidate: 'template static decltype (Method ::operator()) is_brackets_op_defined ::Check(const C&) [with C = C; T = int]' main.cpp:17:42: note: template argument deduction/substitution failed: main.cpp: In substitution of 'template static decltype (Method ::operator()) is_brackets_op_defined ::Check (const C&) [with C = Method ]': Ну вот, в качестве C в функцию Check приходит Method . Очевидно что в таком контексте кострукция Method ::operator() дает ошибку компиляции и для int, и для float. И согласно SFINAE компилятор отбрасывает эту перегрузку. Надо как-то изменить это вражение так, чтобы оно разворачивалось в какой-то тип для int и давало substitution failed для float. На мой взгляд &C::operator() будет в самый раз(код). В принципе на этом можно остановиться, но не могу не предложить чуточку упростить код. В качестве возвращаемых значений можно использовать std::true_type и std::false_type это позволит выкинуть использование std::is_same. UPD. Вопрос в комментарии навел на мысль о втором улучшении. Вместо const C& можно использовать const C*. Это позволит избавится от std::declval, который, по какой-то причине, создает ошибку компиляции с incomplete type под GCC. #include #include template struct Method; template<> struct Method { int operator()() { return 1; } }; template struct is_brackets_op_defined { static std::false_type Check(...); template static std::true_type Check(const C*); using type = decltype(Check(static_cast *>(nullptr))); constexpr static bool value = type(); }; int main() { std::cout << std::boolalpha << is_brackets_op_defined ::value << std::endl; std::cout << std::boolalpha << is_brackets_op_defined ::value << std::endl; } Ответ 2
Ответа почему не работает Ваш код у меня нет, но есть то, что заставит его работать: templatestatic decltype(std::declval ().operator()()) Check(const C&); В Вашем коде есть очевидная проблема: Method ::operator() не корректный C++, т.к. это функция-член с которой ничего не делается. А её нужно либо вызывать, либо брать её адрес. Ответ 3
Можно вот так. Заметьте, что вспомогательная функция тоже является шаблоном: #includetemplate struct is_brackets_op_defined { private: template static char ( & Check(...) )[1]; private: template static char ( & Check(decltype(&C::operator ())) )[2]; public: static constexpr bool value{2 == sizeof(Check (nullptr))}; }; struct ClassWith { void operator ()() {} }; struct ClassWithout { }; int main() { std::cout << is_brackets_op_defined ::value << std::endl; std::cout << is_brackets_op_defined ::value << std::endl; std::cout << is_brackets_op_defined ::value << std::endl; return 0; } online compiler
Комментариев нет:
Отправить комментарий