所以我开始实现一些模仿STL算法的行为,但是使用异构容器a.k.a std :: tuple的算法。类型安全 - all_of/any_of/none_of std ::元组
template<typename UnaryPredicate, typename Tuple>
bool all_of(UnaryPredicate&& p, Tuple&& t) noexcept
{
return std::apply([&p](auto&& ...xs){ return (p(std::forward<decltype(xs)>(xs)) && ...); }, std::forward<Tuple>(t));
}
template<typename UnaryPredicate, typename Tuple>
bool any_of(UnaryPredicate&& p, Tuple&& t) noexcept
{
return std::apply([&p](auto&& ...xs){ return (p(std::forward<decltype(xs)>(xs)) || ...); }, std::forward<Tuple>(t));
}
template<typename UnaryPredicate, typename Tuple>
bool none_of(UnaryPredicate&& p, Tuple&& t) noexcept
{
return std::apply([&p](auto&& ...xs){ return !(p(std::forward<decltype(xs)>(xs)) || ...); }, std::forward<Tuple>(t));
}
如果您使用返回布尔值的UnaryPredicate,所有这些工作正常。但如果它不呢?如何确保UnaryPredicate在与元组的每个元素一起调用时返回布尔值?另外我怎样才能检查UnaryPredicate实际上是否没有抛出任何异常。
我知道有'is_nothrow_invocable'和'invoke_result'这样的类型特征,但所有这些都需要元组包含的元素的类型。我真的必须使用“algorithm_impl”模式吗?
namespace impl
{
template<typename UnaryPredicate, typename Tuple, auto ...Is>
bool all_of_impl(UnaryPredicate&& p, Tuple&& t, std::index_sequence<Is...>) noexcept
{
return std::apply([&p](auto&& ...xs){ return (p(std::forward<decltype(xs)>(xs)) && ...); }, std::forward<Tuple>(t));
}
}
template<typename UnaryPredicate, typename Tuple>
bool all_of(UnaryPredicate&& p, Tuple&& t) noexcept
{
return impl::all_of_impl(std::forward<UnaryPredicate>(p), std::forward<Tuple>(t), std::make_index_sequence<std::tuple_size_v<std::decay_t<Tuple>>>{});
}
现在我可以做这样的事情:
std::enable_if_t<std::conjunction_v<std::is_same<std::invoke_result_t<std::decay_t<UnaryPredicate>, std::tuple_element_t<Is, std::decay_t<Tuple>>>, bool>...>, bool>
但是,这是真正的路要走?
编辑:
好一如既往我过于复杂的事情。我想我找到可接受的解决方案:
template<typename UnaryPredicate, typename Tuple>
struct helper;
template<typename UnaryPredicate, typename Tuple>
struct helper2;
template<typename UnaryPredicate, typename ...Ts>
struct helper<UnaryPredicate, std::tuple<Ts...>>
: std::bool_constant<std::conjunction_v<std::is_same<bool, std::invoke_result_t<std::decay_t<UnaryPredicate>, std::decay_t<Ts>>>...>>
{};
template<typename UnaryPredicate, typename ...Ts>
struct helper2<UnaryPredicate, std::tuple<Ts...>>
: std::bool_constant<std::conjunction_v<std::is_nothrow_invocable<std::decay_t<UnaryPredicate>, std::decay_t<Ts>>...>>
{};
template<typename UnaryPredicate, typename Tuple>
inline constexpr auto helper_v{ helper<UnaryPredicate, Tuple>::value };
template<typename UnaryPredicate, typename Tuple>
inline constexpr auto helper2_v{ helper2<UnaryPredicate, Tuple>::value };
template<typename UnaryPredicate, typename Tuple>
std::enable_if_t<helper_v<UnaryPredicate, Tuple>, bool> all_of(UnaryPredicate&& p, Tuple&& t) noexcept(helper2_v<UnaryPredicate, Tuple>)
{
return std::apply([&p](auto&& ...xs){ return (p(std::forward<decltype(xs)>(xs)) && ...); }, std::forward<Tuple>(t));
}
template<typename UnaryPredicate, typename Tuple>
std::enable_if_t<helper_v<UnaryPredicate, Tuple>, bool> any_of(UnaryPredicate&& p, Tuple&& t) noexcept(helper2_v<UnaryPredicate, Tuple>)
{
return std::apply([&p](auto&& ...xs){ return (p(std::forward<decltype(xs)>(xs)) || ...); }, std::forward<Tuple>(t));
}
template<typename UnaryPredicate, typename Tuple>
std::enable_if_t<helper_v<UnaryPredicate, Tuple>, bool> none_of(UnaryPredicate&& p, Tuple&& t) noexcept(helper2_v<UnaryPredicate, Tuple>)
{
return std::apply([&p](auto&& ...xs){ return !(p(std::forward<decltype(xs)>(xs)) || ...); }, std::forward<Tuple>(t));
}
唯一令天真的实施接受一个更好的解决方案和enable_if'd实现拒绝是重载'operator ||'的返回类型,所以为什么要麻烦? – Caleth
看起来你有两个问题:如何确保UnaryPredicate传递将返回每种类型的'bool',以及如何检查每种类型的UnaryPredicate是否为'noexcept'。那是对的吗? – AndyG
是的,这是正确的@AndyG – Yamahari