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; }));
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;
}
All suggestions are welcome.
The summary information:
- Which question it is a follow-up to? - A recursive_transform for std::vector with various return type and - A recursive_transform Template Function Implementation with std::invocable concept in C++ 
- What changes has been made in the code since last question? - The implementation of - recursive_copy_iftemplate 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. 
