У меня есть функция шаблона, которая по умолчанию не имеет определения, но специализируется на некоторых типах:
template <typename T>
auto foo(bar &, const T &) -> void;
template <>
auto foo<std::string>(bar &, const std::string &) -> void {}
Как мне написать функцию constexpr, которая сообщает мне, имеет ли тип T специализацию для вышеуказанной функции?
Мои лучшие усилия:
namespace detail {
auto has_foo(hana::is_valid([](auto &b, const auto &t) -> decltype(foo(b, t)) {}));
} // namespace detail
template <typename T>
constexpr auto has_foo() -> bool
{
using hana::type_c;
return detail::has_foo(type_c<bar>, type_c<T>);
}
static_assert(has_foo<std::string>());
Однако этот статический огонь возжигает, чего я бы не ожидал, если бы понял это правильно.
Проблема в том, что вы проходите мимо hana::type
с функцией, которая ожидает реальных объектов. Когда ты пишешь detail::has_foo(type_c<bar>, type_c<T>)
Хана проходит hana::type_c
с как есть detail::has_foo
, Но с тех пор foo
не может быть вызван с hana::type
с, это не удается. Вместо этого у вас есть два варианта. Первый вариант — продолжать проходить hana::type
с detail::has_foo
, но использовать declval
внутри has_foo
(обратите внимание, что я добавил соответствующие ref-квалификаторы в bar
а также T
):
#include <boost/hana.hpp>
#include <string>
namespace hana = boost::hana;struct bar { };
template <typename T>
auto foo(bar&, T const&) -> void;
template <>
auto foo<std::string>(bar&, std::string const&) -> void { }
namespace detail {
auto has_foo = hana::is_valid([](auto b, auto t) -> decltype(
foo(hana::traits::declval(b), hana::traits::declval(t))
) { });
}
template <typename T>
constexpr auto has_foo() -> bool {
return detail::has_foo(hana::type_c<bar&>, hana::type_c<T const&>);
}
static_assert(has_foo<std::string>(), "");
Другой вариант — отказаться от использования hana::type
в целом и передать фактические объекты detail::has_foo
:
namespace detail {
auto has_foo = hana::is_valid([](auto& b, auto const& t) -> decltype(foo(b, t)) { });
}
template <typename T>
constexpr auto has_foo() -> decltype(
detail::has_foo(std::declval<bar&>(), std::declval<T const&>())
) { return {}; }
Здесь я использую std::declval
сделать, как будто у меня были объекты надлежащего типа, а затем я вызываю detail::has_foo
с этими «объектами». Какой из них вы выбираете, это в основном вопрос предпочтений. Кроме того, в зависимости от вашего варианта использования, возможно, реальные объекты доступны при вызове has_foo
, Если это так, вы можете рефакторинг
namespace detail {
auto has_foo = hana::is_valid([](auto& b, auto const& t) -> decltype(foo(b, t)) { });
}
template <typename T>
constexpr auto has_foo(bar& b, T const& t) -> decltype(detail::has_foo(b, t)) { return {}; }
C ++ 17 сделает нашу жизнь намного проще, сняв запрет на лямбды в константных выражениях, что позволит вам писать
constexpr auto has_foo = hana::is_valid([](bar& b, auto const& t) -> decltype(foo(b, t)) { });
таким образом устраняя необходимость во внешнем помощнике.
Также обратите внимание, что вы не тестируете foo
имеет специализация за T
но действительно ли foo(...)
выражение хорошо сформировано. Это может незначительно отличаться при наличии перегрузок или ADL, но этого должно быть достаточно для большинства случаев использования. Я не уверен, что можно точно проверить, является ли функция специализированной для какого-либо типа.
Надеюсь это поможет!
Других решений пока нет …