简体   繁体   中英

Boost.Hana: How to check if function has specialisation for a certain type?

I have a template function that has no definition by default but it specialised by some types:

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

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

How do I write a constexpr function that tells me if type T has a specialisation for the above function?

My best effort:

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

This static assert fires, however, which I would expect it not to if I had got this right.

The problem here is that you're passing hana::type s to a function that expects actual objects. When you write detail::has_foo(type_c<bar>, type_c<T>) , Hana passes the hana::type_c s as-is to detail::has_foo . But since foo can't be called with hana::type s, it fails. Instead, you have two options. The first option is to keep on passing hana::type s to detail::has_foo , but to use declval inside has_foo (note that I have added the proper ref-qualifiers to bar and 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>(), "");

The other option is to drop the usage of hana::type altogether and to pass actual objects to 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 {}; }

Here, I'm using std::declval to do as-if I had objects of the proper type, and then I call detail::has_foo with those "objects". Which one you pick is mainly a matter of preference. Also, depending on your use case, perhaps the actual objects are available when you call has_foo . If this is the case, you could refactor to

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 will make our life much easier by removing the ban on lambdas in constant expressions, which will allow you to write

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

thus removing the need for an external helper.

Also note that you're not specifically testing whether foo has a specialization for T , but really whether the foo(...) expression is well-formed. This can be subtly different in the presence of overloads or ADL, but it should be sufficient for most use cases. I'm not sure if it's possible to precisely check whether a function is specialized for some type.

Hope this helps!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM