使用boost::hana::is_valid验证make_foo合法调用遇阻
问题根源
boost::hana::is_valid的判断逻辑是检查表达式能否推导出有效类型,而非验证表达式是否能实际执行。当你传入std::string&&(比如std::move(s)或""s)时,重载决议会匹配到被删除的make_impl<Foo>::apply(std::string&&),虽然这个函数不可调用,但decltype(make_foo(std::move(s)))仍能得到合法的Foo类型,因此is_valid错误地返回true。
解决方案
方案1:C++20及以上(推荐)
结合requires表达式增强检查逻辑,同时验证表达式的可调用性:
constexpr auto can_make_foo_from = hana::is_valid([](auto&& obj) -> decltype(auto) { // 额外检查表达式是否可实际调用 requires requires { make_foo(std::forward<decltype(obj)>(obj)); }; return make_foo(std::forward<decltype(obj)>(obj)); });
方案2:兼容C++17
使用std::is_invocable_v直接判断函数是否可调用:
#include <type_traits> constexpr auto can_make_foo_from = [](auto&& obj) constexpr { using ArgT = decltype(std::forward<decltype(obj)>(obj)); return std::is_invocable_v<decltype(make_foo)&, ArgT>; };
修正后的测试代码(以C++20方案为例)
#include <boost/hana.hpp> #include <string> #include <string_view> namespace hana = boost::hana; struct Foo { explicit constexpr Foo() : text{} {} explicit constexpr Foo(std::string_view text) : text{std::move(text)} {} explicit constexpr Foo(char const* text) : Foo{std::string_view{text}} {} explicit constexpr Foo(char* text) : Foo{std::string_view{text}} {} explicit constexpr Foo(std::string&&) = delete; std::string_view text; }; namespace boost::hana { template <> struct make_impl<Foo> { static constexpr Foo apply(const char* text) { return Foo{text}; } static constexpr Foo apply(std::string const& text) { return Foo{text}; } static constexpr Foo apply(std::string_view text) { return Foo{std::move(text)}; } static constexpr Foo apply(std::string&&) = delete; }; } inline constexpr auto make_foo = boost::hana::make<Foo>; // 修正后的检查逻辑 constexpr auto can_make_foo_from = hana::is_valid([](auto&& obj) -> decltype(auto) { requires requires { make_foo(std::forward<decltype(obj)>(obj)); }; return make_foo(std::forward<decltype(obj)>(obj)); }); int main() { std::string s{}; std::string_view sv{}; static_assert( decltype(can_make_foo_from(""))::value); static_assert( decltype(can_make_foo_from(""sv))::value); static_assert( decltype(can_make_foo_from(sv))::value); static_assert( decltype(can_make_foo_from(s))::value); static_assert(!decltype(can_make_foo_from(std::move(s)))::value); static_assert(!decltype(can_make_foo_from(""s))::value); }
原理说明
- 方案1的
requires表达式会直接验证make_foo(...)是否能合法调用,排除了被删除函数的干扰。 - 方案2的
std::is_invocable_v会检查给定的参数能否调用目标函数,自动忽略被删除或不可访问的重载。
内容的提问来源于stack exchange,提问作者Enlico
相关产品推荐
相关产品推荐

