Boost.Hana: 如何检查函数是否具有特定类型的特化?

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

我有一个模板函数,默认情况下没有定义,但它由某些类型专门化:

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::types 传递给需要实际对象的函数。当您编写 detail::has_foo(type_c<bar>, type_c<T>) 时,Hana 将 hana::type_c 原样传递给 detail::has_foo。但是由于 foo 不能用 hana::type 调用,所以它失败了。相反,您有两个选择。第一个选项是继续将 hana::types 传递给 detail::has_foo,但在 declval inside has_foo 中使用(注意我有将适当的引用限定符添加到 barT):

#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 来做,就像我有正确类型的对象一样,然后我用那些 "objects" 调用 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 将移除常量表达式中对 lambda 的禁令,让我们的生活变得更加轻松,这将允许您编写

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

因此不再需要外部助手。

另请注意,您并不是专门测试 foo 是否具有 Tspecialization,而是真正测试 foo(...) 表达式是否是良构的。这在存在重载或 ADL 的情况下可能会略有不同,但对于大多数用例来说应该足够了。我不确定是否可以精确检查某个函数是否专用于某种类型。

希望对您有所帮助!