/ / Boost.Hana: come verificare se la funzione ha una specializzazione per un determinato tipo? - c ++, boost, c ++ 14, sfinae, boost-hana

Boost.Hana: come verificare se la funzione ha una specializzazione per un determinato tipo? - c ++, boost, c ++ 14, sfinae, boost-hana

Ho una funzione template che non ha definizione per definizione ma è specializzata in alcuni tipi:

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

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

Come scrivo una funzione di constexpr che mi dice se il tipo T ha una specializzazione per la funzione di cui sopra?

Il mio miglior sforzo:

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

Questa affermazione statica spara, tuttavia, che mi sarei aspettato di non farlo se avessi avuto ragione.

risposte:

6 per risposta № 1

Il problema qui è che stai passando hana::types ad una funzione che si aspetta oggetti reali. Quando scrivi detail::has_foo(type_c<bar>, type_c<T>), Hana passa il hana::type_ccosì come è detail::has_foo. Ma da allora foo non si può chiamare con hana::types, fallisce. Invece, hai due opzioni. La prima opzione è continuare a passare hana::types a detail::has_foo, ma da usare declval dentro has_foo (nota che ho aggiunto le qualifiche di riferimento appropriate a bar e 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>(), "");

L'altra opzione è di abbandonare l'uso di hana::type del tutto e per passare oggetti reali a 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 {}; }

Qui, sto usando std::declval fare come-se avessi oggetti del tipo corretto, e poi chiamo detail::has_foo con quegli "oggetti". Quello che scegli è principalmente una questione di preferenza. Inoltre, a seconda del caso d'uso, forse gli oggetti reali sono disponibili quando chiami has_foo. Se questo è il caso, puoi refactoring per

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 renderà la nostra vita molto più semplice rimuovendo il divieto di lambda in espressioni costanti, che ti permetteranno di scrivere

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

rimuovendo così la necessità di un aiutante esterno.

Si noti inoltre che non si sta specificamente testando se foo ha un specializzazione per T, ma davvero se il foo(...) l'espressione è ben formata. Questo può essere sottilmente differente in presenza di sovraccarichi o ADL, ma dovrebbe essere sufficiente per la maggior parte dei casi d'uso. Non sono sicuro che sia possibile verificare con precisione se una funzione è specializzata per qualche tipo.

Spero che questo ti aiuti!