This is a follow-up question for [A recursive_copy_if Template Function Implementation in C++](https://codereview.stackexchange.com/q/254178/231235). Besides the recursive version [`std::ranges::copy_if`](https://en.cppreference.com/w/cpp/algorithm/ranges/copy), I am trying to implement a recursive version [`std::replace_copy_if`](https://en.cppreference.com/w/cpp/algorithm/replace_copy). **The experimental implementation** The experimental implementation is as below. ```C++ // recursive_replace_copy_if implementation template<std::ranges::range Range, std::invocable<std::ranges::range_value_t<Range>> UnaryPredicate, class T> constexpr auto recursive_replace_copy_if(const Range& input, const UnaryPredicate& unary_predicate, const T& new_value) { Range output{}; std::ranges::replace_copy_if( std::ranges::cbegin(input), std::ranges::cend(input), std::inserter(output, std::ranges::end(output)), unary_predicate, new_value); return output; } template<std::ranges::input_range Range, class UnaryPredicate, class T> requires (!std::invocable<UnaryPredicate, std::ranges::range_value_t<Range>>) constexpr auto recursive_replace_copy_if(const Range& input, const UnaryPredicate& unary_predicate, const T& new_value) { Range output{}; std::ranges::transform( std::ranges::cbegin(input), std::ranges::cend(input), std::inserter(output, std::ranges::end(output)), [&unary_predicate, &new_value](auto&& element) { return recursive_replace_copy_if(element, unary_predicate, new_value); } ); return output; } ``` **Test cases** The test cases for `recursive_replace_copy_if` function including `std::vector<int>`, `std::vector<std::vector<int>>`, `std::vector<std::string>`, `std::vector<std::vector<std::string>>`, `std::deque<int>`, `std::deque<std::deque<int>>`, `std::list<int>` and `std::list<std::list<int>>` type input are as below. ```C++ int main() { std::vector<int> test_vector{ 5, 7, 4, 2, 8, 6, 1, 9, 0, 3 }; recursive_print(recursive_replace_copy_if(test_vector, std::bind(std::less<int>(), std::placeholders::_1, 5), 55)); recursive_print(test_vector); std::vector<decltype(test_vector)> test_vector2{ test_vector , test_vector , test_vector }; recursive_print(recursive_replace_copy_if(test_vector2, std::bind(std::less<int>(), std::placeholders::_1, 5), 55)); // std::vector<std::string> std::vector<std::string> test_string_vector{ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20" }; recursive_print( recursive_replace_copy_if( test_string_vector, [](std::string x) { return (x == "1"); }, "11" ) ); // std::vector<std::vector<std::string>> std::vector<decltype(test_string_vector)> test_string_vector2{ test_string_vector , test_string_vector , test_string_vector }; recursive_print( recursive_replace_copy_if( test_string_vector2, [](std::string x) { return (x == "1"); }, "11" ) ); // 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_replace_copy_if(test_deque, [](int x) { return (x % 2) == 0; }, 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_replace_copy_if(test_deque2, [](int x) { return (x % 2) == 0; }, 0)); // std::list<int> std::list<int> test_list = { 1, 2, 3, 4, 5, 6 }; recursive_print(recursive_replace_copy_if(test_list, [](int x) { return (x % 2) == 0; }, 0)); // std::list<std::list<int>> std::list<std::list<int>> test_list2 = { test_list, test_list, test_list, test_list }; recursive_print(recursive_replace_copy_if(test_list2, [](int x) { return (x % 2) == 0; }, 0)); return 0; } ``` The output of the testing code above: ``` Level 0: 5 7 55 55 8 6 55 9 55 55 Level 0: Level 1: 5 7 55 55 8 6 55 9 55 55 Level 1: 5 7 55 55 8 6 55 9 55 55 Level 1: 5 7 55 55 8 6 55 9 55 55 Level 0: Level 1: 0 Level 1: 1 1 Level 1: 2 Level 1: 3 Level 1: 4 Level 1: 5 Level 1: 6 Level 1: 7 Level 1: 8 Level 1: 9 Level 1: 1 0 Level 1: 1 1 Level 1: 1 2 Level 1: 1 3 Level 1: 1 4 Level 1: 1 5 Level 1: 1 6 Level 1: 1 7 Level 1: 1 8 Level 1: 1 9 Level 1: 2 0 Level 0: Level 1: Level 2: 0 Level 2: 1 1 Level 2: 2 Level 2: 3 Level 2: 4 Level 2: 5 Level 2: 6 Level 2: 7 Level 2: 8 Level 2: 9 Level 2: 1 0 Level 2: 1 1 Level 2: 1 2 Level 2: 1 3 Level 2: 1 4 Level 2: 1 5 Level 2: 1 6 Level 2: 1 7 Level 2: 1 8 Level 2: 1 9 Level 2: 2 0 Level 1: Level 2: 0 Level 2: 1 1 Level 2: 2 Level 2: 3 Level 2: 4 Level 2: 5 Level 2: 6 Level 2: 7 Level 2: 8 Level 2: 9 Level 2: 1 0 Level 2: 1 1 Level 2: 1 2 Level 2: 1 3 Level 2: 1 4 Level 2: 1 5 Level 2: 1 6 Level 2: 1 7 Level 2: 1 8 Level 2: 1 9 Level 2: 2 0 Level 1: Level 2: 0 Level 2: 1 1 Level 2: 2 Level 2: 3 Level 2: 4 Level 2: 5 Level 2: 6 Level 2: 7 Level 2: 8 Level 2: 9 Level 2: 1 0 Level 2: 1 1 Level 2: 1 2 Level 2: 1 3 Level 2: 1 4 Level 2: 1 5 Level 2: 1 6 Level 2: 1 7 Level 2: 1 8 Level 2: 1 9 Level 2: 2 0 Level 0: 1 0 3 0 5 0 Level 0: Level 1: 1 0 3 0 5 0 Level 1: 1 0 3 0 5 0 Level 1: 1 0 3 0 5 0 Level 0: 1 0 3 0 5 0 Level 0: Level 1: 1 0 3 0 5 0 Level 1: 1 0 3 0 5 0 Level 1: 1 0 3 0 5 0 Level 1: 1 0 3 0 5 0 ``` <a name="full testing code"></a> <details open><summary><b>Full Testing Code</b></summary> <p> The full testing code: ```C++ // A recursive_replace_copy_if Template Function Implementation in C++ #include <algorithm> #include <array> #include <cassert> #include <chrono> #include <complex> #include <concepts> #include <deque> #include <exception> #include <execution> #include <functional> #include <iostream> #include <iterator> #include <list> #include <map> #include <numeric> #include <optional> #include <ranges> #include <stdexcept> #include <string> #include <type_traits> #include <utility> #include <variant> #include <vector> template<typename T> concept is_inserterable = requires(T x) { std::inserter(x, std::ranges::end(x)); }; #ifdef USE_BOOST_MULTIDIMENSIONAL_ARRAY template<typename T> concept is_multi_array = requires(T x) { x.num_dimensions(); x.shape(); boost::multi_array(x); }; #endif // recursive_depth function implementation template<typename T> constexpr std::size_t recursive_depth() { return 0; } template<std::ranges::input_range Range> constexpr std::size_t recursive_depth() { return recursive_depth<std::ranges::range_value_t<Range>>() + 1; } // 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> 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_count implementation // recursive_count implementation (the version with unwrap_level) template<std::size_t unwrap_level, class T> constexpr auto recursive_count(const T& input, const auto& target) { if constexpr (unwrap_level > 0) { static_assert(unwrap_level <= recursive_depth<T>(), "unwrap level higher than recursion depth of input"); return std::transform_reduce(std::ranges::cbegin(input), std::ranges::cend(input), std::size_t{}, std::plus<std::size_t>(), [&target](auto&& element) { return recursive_count<unwrap_level - 1>(element, target); }); } else { return (input == target) ? 1 : 0; } } // recursive_count implementation (the version without unwrap_level) template<std::ranges::input_range Range> constexpr auto recursive_count(const Range& input, const auto& target) { return recursive_count<recursive_depth<Range>()>(input, target); } // recursive_count implementation (with execution policy) template<class ExPo, std::ranges::input_range Range, typename T> requires (std::is_execution_policy_v<std::remove_cvref_t<ExPo>>) constexpr auto recursive_count(ExPo execution_policy, const Range& input, const T& target) { return std::count(execution_policy, std::ranges::cbegin(input), std::ranges::cend(input), target); } template<class ExPo, std::ranges::input_range Range, typename T> requires (std::is_execution_policy_v<std::remove_cvref_t<ExPo>>) && (std::ranges::input_range<std::ranges::range_value_t<Range>>) constexpr auto recursive_count(ExPo execution_policy, const Range& input, const T& target) { return std::transform_reduce(execution_policy, std::ranges::cbegin(input), std::ranges::cend(input), std::size_t{}, std::plus<std::size_t>(), [execution_policy, target](auto&& element) { return recursive_count(execution_policy, element, target); }); } // recursive_count_if implementation template<class T, std::invocable<T> Pred> constexpr std::size_t recursive_count_if(const T& input, const Pred& predicate) { return predicate(input) ? 1 : 0; } template<std::ranges::input_range Range, class Pred> requires (!std::invocable<Pred, Range>) constexpr auto recursive_count_if(const Range& input, const Pred& predicate) { return std::transform_reduce(std::ranges::cbegin(input), std::ranges::cend(input), std::size_t{}, std::plus<std::size_t>(), [predicate](auto&& element) { return recursive_count_if(element, predicate); }); } // recursive_count_if implementation (with execution policy) template<class ExPo, class T, std::invocable<T> Pred> requires (std::is_execution_policy_v<std::remove_cvref_t<ExPo>>) constexpr std::size_t recursive_count_if(ExPo execution_policy, const T& input, const Pred& predicate) { return predicate(input) ? 1 : 0; } template<class ExPo, std::ranges::input_range Range, class Pred> requires ((std::is_execution_policy_v<std::remove_cvref_t<ExPo>>) && (!std::invocable<Pred, Range>)) constexpr auto recursive_count_if(ExPo execution_policy, const Range& input, const Pred& predicate) { return std::transform_reduce(execution_policy, std::ranges::cbegin(input), std::ranges::cend(input), std::size_t{}, std::plus<std::size_t>(), [predicate](auto&& element) { return recursive_count_if(element, predicate); }); } // recursive_count_if implementation (the version with unwrap_level) template<std::size_t unwrap_level, std::ranges::range T, class Pred> auto recursive_count_if(const T& input, const Pred& predicate) { if constexpr (unwrap_level > 1) { return std::transform_reduce(std::ranges::cbegin(input), std::ranges::cend(input), std::size_t{}, std::plus<std::size_t>(), [predicate](auto&& element) { return recursive_count_if<unwrap_level - 1>(element, predicate); }); } else { return std::count_if(std::ranges::cbegin(input), std::ranges::cend(input), predicate); } } // recursive_print implementation template<typename T> constexpr void recursive_print(const T& input, const std::size_t level = 0) { std::cout << std::string(level, ' ') << input << '\n'; } template<std::ranges::input_range Range> constexpr void recursive_print(const Range& input, const std::size_t level = 0) { std::cout << std::string(level, ' ') << "Level " << level << ":" << "\n"; std::ranges::for_each(input, [level](auto&& element) { recursive_print(element, level + 1); }); } // recursive_replace_copy_if implementation template<std::ranges::range Range, std::invocable<std::ranges::range_value_t<Range>> UnaryPredicate, class T> constexpr auto recursive_replace_copy_if(const Range& input, const UnaryPredicate& unary_predicate, const T& new_value) { Range output{}; std::ranges::replace_copy_if( std::ranges::cbegin(input), std::ranges::cend(input), std::inserter(output, std::ranges::end(output)), unary_predicate, new_value); return output; } template<std::ranges::input_range Range, class UnaryPredicate, class T> requires (!std::invocable<UnaryPredicate, std::ranges::range_value_t<Range>>) constexpr auto recursive_replace_copy_if(const Range& input, const UnaryPredicate& unary_predicate, const T& new_value) { Range output{}; std::ranges::transform( std::ranges::cbegin(input), std::ranges::cend(input), std::inserter(output, std::ranges::end(output)), [&unary_predicate, &new_value](auto&& element) { return recursive_replace_copy_if(element, unary_predicate, new_value); } ); return output; } // recursive_size implementation template<class T> requires (!std::ranges::range<T>) constexpr auto recursive_size(const T& input) { return 1; } template<std::ranges::range Range> requires (!(std::ranges::input_range<std::ranges::range_value_t<Range>>)) constexpr auto recursive_size(const Range& input) { return std::ranges::size(input); } template<std::ranges::range Range> requires (std::ranges::input_range<std::ranges::range_value_t<Range>>) constexpr auto recursive_size(const Range& input) { return std::transform_reduce(std::ranges::begin(input), std::end(input), std::size_t{}, std::plus<std::size_t>(), [](auto& element) { return recursive_size(element); }); } // recursive_transform implementation // recursive_invoke_result_t implementation // from https://stackoverflow.com/a/65504127/6667035 // recursive_invoke_result_t implementation template<std::size_t, typename, typename> struct recursive_invoke_result { }; template<typename T, typename F> struct recursive_invoke_result<0, F, T> { using type = std::invoke_result_t<F, T>; }; template<std::size_t unwrap_level, std::copy_constructible F, template<typename...> typename Container, typename... Ts> requires (std::ranges::input_range<Container<Ts...>> && requires { typename recursive_invoke_result<unwrap_level - 1, F, std::ranges::range_value_t<Container<Ts...>>>::type; }) struct recursive_invoke_result<unwrap_level, F, Container<Ts...>> { using type = Container<typename recursive_invoke_result<unwrap_level - 1, F, std::ranges::range_value_t<Container<Ts...>>>::type>; }; template<std::size_t unwrap_level, std::copy_constructible F, typename T> using recursive_invoke_result_t = typename recursive_invoke_result<unwrap_level, F, T>::type; template <std::ranges::range Range> constexpr auto get_output_iterator(Range& output) { return std::inserter(output, std::ranges::end(output)); } template<std::size_t dim, class T> constexpr auto n_dim_vector_generator(T input, std::size_t times) { if constexpr (dim == 0) { return input; } else { auto element = n_dim_vector_generator<dim - 1>(input, times); std::vector<decltype(element)> output(times, element); return output; } } template<std::size_t dim, std::size_t times, class T> constexpr auto n_dim_array_generator(T input) { if constexpr (dim == 0) { return input; } else { auto element = n_dim_array_generator<dim - 1, times>(input); std::array<decltype(element), times> output; std::fill(std::begin(output), std::end(output), element); return output; } } template<std::size_t dim, class T> constexpr auto n_dim_deque_generator(T input, std::size_t times) { if constexpr (dim == 0) { return input; } else { auto element = n_dim_deque_generator<dim - 1>(input, times); std::deque<decltype(element)> output(times, element); return output; } } template<std::size_t dim, class T> constexpr auto n_dim_list_generator(T input, std::size_t times) { if constexpr (dim == 0) { return input; } else { auto element = n_dim_list_generator<dim - 1>(input, times); std::list<decltype(element)> output(times, element); return output; } } template<std::size_t dim, template<class...> class Container = std::vector, class T> constexpr auto n_dim_container_generator(T input, std::size_t times) { if constexpr (dim == 0) { return input; } else { return Container(times, n_dim_container_generator<dim - 1, Container, T>(input, times)); } } int main() { // std::vector<int> std::vector<int> test_vector{ 5, 7, 4, 2, 8, 6, 1, 9, 0, 3 }; recursive_print(recursive_replace_copy_if(test_vector, std::bind(std::less<int>(), std::placeholders::_1, 5), 55)); // std::vector<std::vector<int>> std::vector<decltype(test_vector)> test_vector2 = { test_vector, test_vector, test_vector }; recursive_print(recursive_replace_copy_if(test_vector2, std::bind(std::less<int>(), std::placeholders::_1, 5), 55)); // std::vector<std::string> std::vector<std::string> test_string_vector{ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20" }; recursive_print( recursive_replace_copy_if( test_string_vector, [](std::string x) { return (x == "1"); }, "11" ) ); // std::vector<std::vector<std::string>> std::vector<decltype(test_string_vector)> test_string_vector2{ test_string_vector , test_string_vector , test_string_vector }; recursive_print( recursive_replace_copy_if( test_string_vector2, [](std::string x) { return (x == "1"); }, "11" ) ); // 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_replace_copy_if(test_deque, [](int x) { return (x % 2) == 0; }, 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_replace_copy_if(test_deque2, [](int x) { return (x % 2) == 0; }, 0)); // std::list<int> std::list<int> test_list = { 1, 2, 3, 4, 5, 6 }; recursive_print(recursive_replace_copy_if(test_list, [](int x) { return (x % 2) == 0; }, 0)); // std::list<std::list<int>> std::list<std::list<int>> test_list2 = { test_list, test_list, test_list, test_list }; recursive_print(recursive_replace_copy_if(test_list2, [](int x) { return (x % 2) == 0; }, 0)); return 0; } ``` </p> </details> [A Godbolt link is here.](https://godbolt.org/z/h8vGWd5cj) All suggestions are welcome. The summary information: - Which question it is a follow-up to? [A recursive_copy_if Template Function Implementation in C++](https://codereview.stackexchange.com/q/254178/231235) - What changes has been made in the code since last question? The implementation of `recursive_replace_copy_if` template function is the main idea in this question. - Why a new review is being asked for? If there is any possible improvement, please let me know.