2017-05-29 45 views
1

所以我开始实现一些模仿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)); 
} 
+0

唯一令天真的实施接受一个更好的解决方案和enable_if'd实现拒绝是重载'operator ||'的返回类型,所以为什么要麻烦? – Caleth

+0

看起来你有两个问题:如何确保UnaryPredicate传递将返回每种类型的'bool',以及如何检查每种类型的UnaryPredicate是否为'noexcept'。那是对的吗? – AndyG

+0

是的,这是正确的@AndyG – Yamahari

回答

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

+0

你能解释这个宏是如何工作的? – Yamahari

+0

@Yamahari它会复制你传给它的任何内容,并在这3个'__VA_ARGS__'点重复3次。什么让你困惑? – Yakk

+0

我从来没有在我的代码中使用宏:)什么是__VA_ARGS__? – Yamahari