输入安全性 - all_of / any_of / none_of for std :: tuple

时间:2017-05-29 16:05:40

标签: c++ template-meta-programming c++17

所以我开始实现一些模仿STL算法行为的算法,但是使用异构容器a.k.a std :: tuple。

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

2 个答案:

答案 0 :(得分:1)

#define RETURNS(...) \
  noexcept(noexcept(__VA_ARGS__)) \
  -> decltype(__VA_ARGS__) \
  { return __VA_ARGS__; }

template<class UnaryPredicate, class Tuple>
auto all_of(UnaryPredicate&& p, Tuple&& t)
RETURNS(
  std::apply(
    [&p](auto&& ...xs){
      return (p(std::forward<decltype(xs)>(xs)) && ...);
    },
    std::forward<Tuple>(t)
  )
)

答案 1 :(得分:0)

我找到了比我之前的编辑更好的解决方案:)

#include <functional>
#include <iostream>
#include <tuple>
#include <type_traits>
#include <utility>

template<typename UnaryFunction, typename ...Types>
constexpr std::enable_if_t<std::conjunction_v<std::is_same<std::invoke_result_t<UnaryFunction, Types>, bool>...>, bool> AllOf(UnaryFunction&& p, std::tuple<Types...>&& t) noexcept(std::conjunction_v<std::is_nothrow_invocable<UnaryFunction, Types>...>)
{
    return std::apply([&](auto&& ...xs) constexpr { return (p(std::forward<decltype(xs)>(xs)) && ...); }, std::forward<std::tuple<Types...>>(t));
}

template<typename UnaryFunction, typename ...Types>
constexpr std::enable_if_t<std::conjunction_v<std::is_same<std::invoke_result_t<UnaryFunction, Types>, bool>...>, bool> AnyOf(UnaryFunction&& p, std::tuple<Types...>&& t) noexcept(std::conjunction_v<std::is_nothrow_invocable<UnaryFunction, Types>...>)
{
    return std::apply([&](auto&& ...xs) constexpr { return (p(std::forward<decltype(xs)>(xs)) || ...); }, std::forward<std::tuple<Types...>>(t));
}

template<typename UnaryFunction, typename ...Types>
constexpr std::enable_if_t<std::conjunction_v<std::is_same<std::invoke_result_t<UnaryFunction, Types>, bool>...>, bool> NoneOf(UnaryFunction&& p, std::tuple<Types...>&& t) noexcept(std::conjunction_v<std::is_nothrow_invocable<UnaryFunction, Types>...>)
{
    return std::apply([&](auto&& ...xs) constexpr { return !(p(std::forward<decltype(xs)>(xs)) || ...); }, std::forward<std::tuple<Types...>>(t));
}

int main()
{  
    std::cout 
        << std::boolalpha 
        << AllOf([](auto&& x){ return x; }, std::make_tuple(true, true, true)) << "\n"
        << AnyOf([](auto&& x){ return x; }, std::make_tuple(false, false, true)) << "\n"
        << NoneOf([](auto&& x){ return x; }, std::make_tuple(false, false, false)) << "\n";
}