2
\$\begingroup\$

This is a follow-up question for A recursive_transform for std::vector with various return type and A recursive_transform Template Function Implementation with std::invocable concept in C++. Besides the recursive version std::ranges::transform, I am trying to implement a recursive version std::ranges::copy_if.

The experimental implementation

The experimental implementation is as below.

// recursive_copy_if function template <std::ranges::input_range Range, std::invocable<std::ranges::range_value_t<Range>> UnaryPredicate> constexpr auto recursive_copy_if(const Range& input, const UnaryPredicate& unary_predicate) { Range output{}; std::ranges::copy_if(std::ranges::cbegin(input), std::ranges::cend(input), std::inserter(output, std::ranges::end(output)), unary_predicate); return output; } template < std::ranges::input_range Range, class UnaryPredicate> requires (!std::invocable<UnaryPredicate, std::ranges::range_value_t<Range>>) constexpr auto recursive_copy_if(const Range& input, const UnaryPredicate& unary_predicate) { Range output{}; std::ranges::transform( std::ranges::cbegin(input), std::ranges::cend(input), std::inserter(output, std::ranges::end(output)), [&unary_predicate](auto&& element) { return recursive_copy_if(element, unary_predicate); } ); return output; } 

Test cases

// std::vector<int> std::vector<int> test_vector = { 1, 2, 3, 4, 5, 6 }; recursive_print(recursive_copy_if(test_vector, [](int x) { return (x % 2) == 0; })); // std::vector<std::vector<int>> std::vector<decltype(test_vector)> test_vector2 = { test_vector, test_vector, test_vector }; recursive_print(recursive_copy_if(test_vector2, [](int x) { return (x % 2) == 0; })); // std::vector<std::string> recursive_print( recursive_copy_if( recursive_transform(test_vector, [](int x) { return std::to_string(x); }), [](std::string x) { return (x == "1"); } ) ); // std::vector<std::vector<std::string>> recursive_print( recursive_copy_if( recursive_transform(test_vector2, [](int x) { return std::to_string(x); }), [](std::string x) { return (x == "1"); } ) ); // std::deque<int> std::deque<int> test_deque; test_deque.push_back(1); test_deque.push_back(2); test_deque.push_back(3); test_deque.push_back(4); test_deque.push_back(5); test_deque.push_back(6); recursive_print(recursive_copy_if(test_deque, [](int x) { return (x % 2) == 0; })); // std::deque<std::deque<int>> std::deque<decltype(test_deque)> test_deque2; test_deque2.push_back(test_deque); test_deque2.push_back(test_deque); test_deque2.push_back(test_deque); recursive_print(recursive_copy_if(test_deque2, [](int x) { return (x % 2) == 0; })); // std::list<int> std::list<int> test_list = { 1, 2, 3, 4, 5, 6 }; recursive_print(recursive_copy_if(test_list, [](int x) { return (x % 2) == 0; })); // std::list<std::list<int>> std::list<std::list<int>> test_list2 = { test_list, test_list, test_list, test_list }; recursive_print(recursive_copy_if(test_list2, [](int x) { return (x % 2) == 0; })); 

Full Testing Code

The full testing code:

#include <algorithm> #include <array> #include <cassert> #include <chrono> #include <complex> #include <concepts> #include <deque> #include <execution> #include <exception> #include <functional> #include <iostream> #include <iterator> #include <list> #include <map> #include <mutex> #include <numeric> #include <optional> #include <ranges> #include <stdexcept> #include <string> #include <tuple> #include <type_traits> #include <utility> #include <variant> #include <vector> // recursive_copy_if function template <std::ranges::input_range Range, std::invocable<std::ranges::range_value_t<Range>> UnaryPredicate> constexpr auto recursive_copy_if(const Range& input, const UnaryPredicate& unary_predicate) { Range output{}; std::ranges::copy_if(std::ranges::cbegin(input), std::ranges::cend(input), std::inserter(output, std::ranges::end(output)), unary_predicate); return output; } template < std::ranges::input_range Range, class UnaryPredicate> requires (!std::invocable<UnaryPredicate, std::ranges::range_value_t<Range>>) constexpr auto recursive_copy_if(const Range& input, const UnaryPredicate& unary_predicate) { Range output{}; std::ranges::transform( std::ranges::cbegin(input), std::ranges::cend(input), std::inserter(output, std::ranges::end(output)), [&unary_predicate](auto&& element) { return recursive_copy_if(element, unary_predicate); } ); return output; } // recursive_print implementation // https://codereview.stackexchange.com/q/251208/231235 template<std::ranges::input_range Range> constexpr auto recursive_print(const Range& input, const int level = 0) { auto output = input; std::cout << std::string(level, ' ') << "Level " << level << ":" << std::endl; std::transform(std::ranges::cbegin(input), std::ranges::cend(input), std::ranges::begin(output), [level](auto&& x) { std::cout << std::string(level, ' ') << x << std::endl; return x; } ); return output; } template<std::ranges::input_range Range> requires (std::ranges::input_range<std::ranges::range_value_t<Range>>) constexpr auto recursive_print(const Range& input, const int level = 0) { auto output = input; std::cout << std::string(level, ' ') << "Level " << level << ":" << std::endl; std::transform(std::ranges::cbegin(input), std::ranges::cend(input), std::ranges::begin(output), [level](auto&& element) { return recursive_print(element, level + 1); } ); return output; } // recursive_invoke_result_t implementation template<typename, typename> struct recursive_invoke_result { }; template<typename T, std::invocable<T> F> struct recursive_invoke_result<F, T> { using type = std::invoke_result_t<F, T>; }; template<typename F, template<typename...> typename Container, typename... Ts> requires ( !std::invocable<F, Container<Ts...>> && std::ranges::input_range<Container<Ts...>> && requires { typename recursive_invoke_result<F, std::ranges::range_value_t<Container<Ts...>>>::type; }) struct recursive_invoke_result<F, Container<Ts...>> { using type = Container<typename recursive_invoke_result<F, std::ranges::range_value_t<Container<Ts...>>>::type>; }; template<typename F, typename T> using recursive_invoke_result_t = typename recursive_invoke_result<F, T>::type; // recursive_transform implementation template <class T, std::invocable<T> F> constexpr auto recursive_transform(const T& input, const F& f) { return f(input); } template < std::ranges::input_range Range, class F> requires (!std::invocable<F, Range>) constexpr auto recursive_transform(const Range& input, const F& f) { recursive_invoke_result_t<F, Range> output{}; std::ranges::transform( std::ranges::cbegin(input), std::ranges::cend(input), std::inserter(output, std::ranges::end(output)), [&f](auto&& element) { return recursive_transform(element, f); } ); return output; } int main() { // std::vector<int> std::vector<int> test_vector = { 1, 2, 3, 4, 5, 6 }; recursive_print(recursive_copy_if(test_vector, [](int x) { return (x % 2) == 0; })); // std::vector<std::vector<int>> std::vector<decltype(test_vector)> test_vector2 = { test_vector, test_vector, test_vector }; recursive_print(recursive_copy_if(test_vector2, [](int x) { return (x % 2) == 0; })); // std::vector<std::string> recursive_print( recursive_copy_if( recursive_transform(test_vector, [](int x) { return std::to_string(x); }), [](std::string x) { return (x == "1"); } ) ); // std::vector<std::vector<std::string>> recursive_print( recursive_copy_if( recursive_transform(test_vector2, [](int x) { return std::to_string(x); }), [](std::string x) { return (x == "1"); } ) ); // std::deque<int> std::deque<int> test_deque; test_deque.push_back(1); test_deque.push_back(2); test_deque.push_back(3); test_deque.push_back(4); test_deque.push_back(5); test_deque.push_back(6); recursive_print(recursive_copy_if(test_deque, [](int x) { return (x % 2) == 0; })); // std::deque<std::deque<int>> std::deque<decltype(test_deque)> test_deque2; test_deque2.push_back(test_deque); test_deque2.push_back(test_deque); test_deque2.push_back(test_deque); recursive_print(recursive_copy_if(test_deque2, [](int x) { return (x % 2) == 0; })); // std::list<int> std::list<int> test_list = { 1, 2, 3, 4, 5, 6 }; recursive_print(recursive_copy_if(test_list, [](int x) { return (x % 2) == 0; })); // std::list<std::list<int>> std::list<std::list<int>> test_list2 = { test_list, test_list, test_list, test_list }; recursive_print(recursive_copy_if(test_list2, [](int x) { return (x % 2) == 0; })); return 0; } 

A Godbolt link is here.

All suggestions are welcome.

The summary information:

\$\endgroup\$

    1 Answer 1

    1
    \$\begingroup\$

    You don't need this line:

    requires (!std::invocable<UnaryPredicate, std::ranges::range_value_t<Range>>) 

    And your test cases miss a std::vector<std::string> as input, but your recursive_copy_if() works fine on those as well.

    \$\endgroup\$

      Start asking to get answers

      Find the answer to your question by asking.

      Ask question

      Explore related questions

      See similar questions with these tags.