Как я могу использовать Boost.Hana, чтобы определить, есть ли у функтора оператор вызова, который может быть вызван с определенным аргументом шаблона?
В моем приложении я хочу во время компиляции определить, является ли произвольный тип функтора Func
имеет нулевой оператор вызова, который может быть вызван с заданным явным аргументом шаблона T
, Основываясь на предыдущем ответе SO, который я нашел, я придумал следующее:
#include <boost/hana.hpp>
#include <iostream>
#include <type_traits>
namespace hana = boost::hana;
namespace detail
{
template <typename T>
auto can_call = hana::is_valid([](auto &&f) ->
decltype(f.template operator()<T>()) { });
}
template <typename Func, typename T>
constexpr auto can_call() ->
decltype(detail::can_call<typename std::remove_reference<T>::type>(
std::declval<Func>())) { return {}; }
struct foo
{
template <typename T, typename =
std::enable_if_t<!std::is_same<T, char>::value>>
void operator()() const { }
};
int main()
{
std::cout << "char: " << can_call<foo, char>() << std::endl;
std::cout << "int: " << can_call<foo, int>() << std::endl;
}
Я ожидаю, что этот пример распечатает:
char: 0
int: 1
Так как char
Тип аргумента шаблона явно enable_if
в foo
, Я пробовал следующие компиляторы:
- Apple clang v8.0.0: пример компилируется и работает, как и ожидалось.
- mainline clang v3.9.1 + (через Wandbox): пример компилируется и запускается, как и ожидалось.
- mainline clang v3.6.0 - v3.8.1 (через Wandbox): компилятор умирает с внутренней ошибкой.
магистраль g++ 7.0, 20170410 (через Wandbox): компиляция завершается со следующими ошибками:
dd.cc: In instantiation of ‘auto detail::can_call<char>’: dd.cc:15:14: required by substitution of ‘template<class Func, class T> constexpr decltype (can_call<typename std::remove_reference<_To>::type>(declval<Func>())) can_call() [with Func = foo; T = char]’ dd.cc:25:50: required from here dd.cc:10:10: error: ‘auto detail::can_call<char>’ has incomplete type auto can_call = hana::is_valid([](auto &&f) -> decltype(f.template operator()<T>()) { }); ^~~~~~~~ dd.cc: In function ‘int main()’: dd.cc:25:50: error: no matching function for call to ‘can_call<foo, char>()’ std::cout << "char: " << can_call<foo, char>() << std::endl; ^ dd.cc:14:16: note: candidate: template<class Func, class T> constexpr decltype (can_call<typename std::remove_reference<_To>::type>(declval<Func>())) can_call() constexpr auto can_call() -> ^~~~~~~~ dd.cc:14:16: note: substitution of deduced template arguments resulted in errors seen above dd.cc: In instantiation of ‘auto detail::can_call<int>’: dd.cc:15:14: required by substitution of ‘template<class Func, class T> constexpr decltype (can_call<typename std::remove_reference<_To>::type>(declval<Func>())) can_call() [with Func = foo; T = int]’ dd.cc:26:48: required from here dd.cc:10:10: error: ‘auto detail::can_call<int>’ has incomplete type auto can_call = hana::is_valid([](auto &&f) -> decltype(f.template operator()<T>()) { }); ^~~~~~~~ dd.cc:26:48: error: no matching function for call to ‘can_call<foo, int>()’ std::cout << "int: " << can_call<foo, int>() << std::endl; ^ dd.cc:14:16: note: candidate: template<class Func, class T> constexpr decltype (can_call<typename std::remove_reference<_To>::type>(declval<Func>())) can_call() constexpr auto can_call() -> ^~~~~~~~ dd.cc:14:16: note: substitution of deduced template arguments resulted in errors seen above
Кажется, не нравится мое использование hana::is_valid()
определить, существует ли указанный оператор. Тем не менее, я думаю, что способ, которым я его использую, соответствует его предполагаемому использованию.
Это ошибка в gcc, более мягкая реализация в современных версиях clang, или я неправильно реализовал этот тип проверки? Кажется, что это определенно в рулевой рубке Ханы; Я просто пытаюсь обернуть голову вокруг своей новой модели constexpr
метапрограммированием.
1 ответ
Вот обходной путь, который использует структуру "функтор" вместо лямбды и дополнительный уровень косвенности для типа is_valid
экземпляр, чтобы успокоить GCC.
namespace detail
{
template <typename T>
struct check_can_call {
template <typename F>
constexpr auto operator()(F&& f) ->
decltype(f.template operator()<T>()) { }
};
template <typename T>
using is_call_valid = decltype(hana::is_valid(check_can_call<T>{}));
template <typename T>
constexpr is_call_valid<T> can_call{};
}