这里的问题是您将hana::type
s 传递给需要实际对象的函数。当您编写 时detail::has_foo(type_c<bar>, type_c<T>)
,Hana 将hana::type_c
s 按原样传递给detail::has_foo
. 但是由于foo
不能用hana::type
s 调用,所以它失败了。相反,您有两个选择。第一个选项是继续将hana::type
s 传递给detail::has_foo
,但在declval
内部 has_foo
使用(请注意,我已将正确的引用限定符添加到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>(), "");
另一种选择是完全放弃使用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
做,好像我有正确类型的对象,然后我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
有专门化T
,而是真正测试foo(...)
表达式是否格式正确。在存在重载或 ADL 的情况下,这可能会略有不同,但对于大多数用例来说应该足够了。我不确定是否可以精确检查一个函数是否专门用于某种类型。
希望这可以帮助!