Страницы

Поиск по вопросам

воскресенье, 1 декабря 2019 г.

Метафункция, для определения существования специализации функтора для данного в шаблоне типа

#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



Ответа почему не работает Ваш код у меня нет, но есть то, что заставит его работать: template static decltype(std::declval().operator()()) Check(const C&); В Вашем коде есть очевидная проблема: Method::operator() не корректный C++, т.к. это функция-член с которой ничего не делается. А её нужно либо вызывать, либо брать её адрес.

Ответ 3



Можно вот так. Заметьте, что вспомогательная функция тоже является шаблоном: #include template 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

Комментариев нет:

Отправить комментарий