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 № 1Il problema qui è che stai passando hana::type
s ad una funzione che si aspetta oggetti reali. Quando scrivi detail::has_foo(type_c<bar>, type_c<T>)
, Hana passa il hana::type_c
così come è detail::has_foo
. Ma da allora foo
non si può chiamare con hana::type
s, fallisce. Invece, hai due opzioni. La prima opzione è continuare a passare hana::type
s 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!