This is a follow-up question for A recursive_count_if Function For Various Type Arbitrary Nested Iterable Implementation in C++. Thanks to Quuxplusone's answer and G. Sliepen's comments. Based on the mentioned std::vector<std::vector<std::string>>
case, it indicates a problem about the process of unwrapping nested iterable structure. In order to make recursive_count_if
function be more generic, I am trying to implement another version of recursive_count_if
template function with checking container's value_type matches specified type. The process of unwrapping nested iterable structure would keep running until the base type in container is as same as the given type T1
.
// recursive_count_if implementation
template<class T1, class T2, class T3> requires (is_iterable<T2> && std::same_as<std::iter_value_t<T2>, T1>)
auto recursive_count_if(const T2& input, const T3 predicate)
{
return std::count_if(input.begin(), input.end(), predicate);
}
// transform_reduce version
template<class T1, class T2, class T3> requires (is_iterable<T2> && !std::same_as<std::iter_value_t<T2>, T1>)
auto recursive_count_if(const T2& input, const T3 predicate)
{
return std::transform_reduce(std::begin(input), std::end(input), std::size_t{}, std::plus<std::size_t>(), [predicate](auto& element) {
return recursive_count_if<T1>(element, predicate);
});
}
The test cases for this version recursive_count_if
template function:
// std::vector<std::vector<int>> case
std::vector<int> test_vector{ 1, 2, 3, 4, 4, 3, 7, 8, 9, 10 };
std::vector<decltype(test_vector)> test_vector2;
test_vector2.push_back(test_vector);
test_vector2.push_back(test_vector);
test_vector2.push_back(test_vector);
// use a lambda expression to count elements divisible by 3.
int num_items1 = recursive_count_if<int>(test_vector2, [](int i) {return i % 3 == 0; });
std::cout << "#number divisible by three: " << num_items1 << '\n';
// std::deque<std::deque<int>> case
std::deque<int> test_deque;
test_deque.push_back(1);
test_deque.push_back(2);
test_deque.push_back(3);
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);
// use a lambda expression to count elements divisible by 3.
int num_items2 = recursive_count_if<int>(test_deque2, [](int i) {return i % 3 == 0; });
std::cout << "#number divisible by three: " << num_items2 << '\n';
// std::vector<std::vector<std::string>> case
std::vector<std::vector<std::string>> v = { {"hello"}, {"world"} };
auto size5 = [](std::string s) { return s.size() == 5; };
auto n = recursive_count_if<std::string>(v, size5);
std::cout << "n:" << n << std::endl;
The output of std::vector<std::vector<std::string>>
case is:
n:2
Besides the std::vector<std::vector<std::string>>
case, you can also play this recursive_count_if
template function like this:
[test_vector2
is from the above usage]
// std::vector<std::vector<std::vector<int>>> case
std::vector<decltype(test_vector2)> test_vector3;
test_vector3.push_back(test_vector2);
test_vector3.push_back(test_vector2);
test_vector3.push_back(test_vector2);
std::cout << recursive_count_if<decltype(test_vector2)>(test_vector3,
[test_vector2](auto& element)
{
return std::equal(element.begin(), element.end(), test_vector2.begin());
}) << std::endl;
All suggestions are welcome.
The summary information:
Which question it is a follow-up to?
A recursive_count_if Function For Various Type Arbitrary Nested Iterable Implementation in C++
What changes has been made in the code since last question?
The previous version
recursive_count_if
template function is hard to deal withstd::vector<std::vector<std::string>>
case. The experimental improved code has been proposed here.Why a new review is being asked for?
In order to perform container's value_type matching structure, the
std::same_as
syntax andstd::iter_value_t
syntax are used here. The getting container's value_type part is handled bystd::iter_value_t
and the type matching part is handled bystd::same_as
. I am not sure if this is a good implementation.On the other hand, although the separated template parameter
T1
plays the role of termination condition well, I am trying to deduce the typeT1
form the given SFINAE-friendly lambda function (const T3 predicate
here, assume that the given lambda function is SFINAE-friendly without something includingauto
syntax) so that making the following usage possible (just like the same usage in Quuxplusone's answer ).// std::vector<std::vector<std::string>> case std::vector<std::vector<std::string>> v = { {"hello"}, {"world"} }; auto size5 = [](std::string s) { return s.size() == 5; }; auto n = recursive_count_if(v, size5); // the `<std::string>` is no needed to pass in again. std::cout << "n:" << n << std::endl;
I've checked some discussions about getting the type of a lambda argument, including Can we get the type of a lambda argument?, Is it possible to retrieve the argument types from a (Functor member's) function signature for use in a template?, Getting the type of lambda arguments and C++ template deduction from lambda. I think that I have no idea about how to deduce the type
T1
automatically. Is it possible done withstd::function
like the experimental code as below?template<class T1, class T2, class T3> requires (is_iterable<T1> && std::same_as<std::iter_value_t<T1>, T3>) auto recursive_count_if(const T1& input, const std::function<T2(T3)> predicate) { //... }
However, I think that maybe
T3
is not the input type of the passed lambda function in the above structure based on some experiments. If I misunderstand something, please tell me. Moreover, please give me some hints or examples if there is any other better way.If there is any further possible improvement, please let me know.