/ / Boost.Hana: Jak sprawdzić, czy funkcja ma specjalizację dla określonego typu? - c ++, boost, c ++ 14, sfinae, boost-hana

Boost.Hana: Jak sprawdzić, czy funkcja ma specjalizację dla określonego typu? - c ++, boost, c ++ 14, sfinae, boost-hana

Mam funkcję szablonu, która domyślnie nie ma definicji, ale specjalizuje się w niektórych typach:

template <typename T>
auto foo(bar &, const T &) -> void;

template <>
auto foo<std::string>(bar &, const std::string &) -> void {}

Jak napisać funkcję constexpr, która mówi mi, czy typ T ma specjalizację dla powyższej funkcji?

Mój najlepszy wysiłek:

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>());

Ten statyczny asert obstaje jednak, czego bym się nie spodziewał, gdybym miał to prawo.

Odpowiedzi:

6 dla odpowiedzi № 1

Problem polega na tym, że ty przechodzisz hana::types do funkcji, która oczekuje rzeczywistych obiektów. Kiedy piszesz detail::has_foo(type_c<bar>, type_c<T>), Hana przechodzi przez hana::type_cs jak-jest detail::has_foo. Lecz odkąd foo nie można się z nim połączyć hana::types, nie działa. Zamiast tego masz dwie opcje. Pierwszą opcją jest kontynuowanie hana::types do detail::has_foo, ale do użycia declval wewnątrz has_foo (zauważ, że dodałem odpowiednie kwalifikatory ref bar i 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>(), "");

Inną opcją jest zrzucenie użycia hana::type łącznie i przekazać rzeczywiste obiekty do 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 {}; }

Tutaj używam std::declval robić to - gdybym miał obiekty odpowiedniego typu, a potem zadzwoniłbym detail::has_foo z tymi "obiektami". To, które wybierasz, to głównie kwestia preferencji. Ponadto, w zależności od przypadku użycia, być może rzeczywiste obiekty są dostępne, gdy zadzwonisz has_foo. Jeśli tak jest, możesz dokonać refaktoryzacji

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 znacznie ułatwi nam życie, usuwając zakaz używania lambdas w stałych wyrażeniach, co pozwoli ci pisać

constexpr auto has_foo = hana::is_valid([](bar& b, auto const& t) -> decltype(foo(b, t)) { });

w ten sposób eliminując potrzebę zewnętrznego pomocnika.

Zauważ też, że nie testujesz dokładnie, czy foo ma specjalizacja dla T, ale tak naprawdę, czy foo(...) ekspresja jest dobrze uformowana. Może to być nieco inne w przypadku przeciążenia lub ADL, ale powinno być wystarczające dla większości przypadków użycia. Nie jestem pewien, czy możliwe jest dokładne sprawdzenie, czy funkcja jest wyspecjalizowana dla jakiegoś typu.

Mam nadzieję że to pomoże!