I noticed that I can seemingly get the same logic(copy elements matching some predicate into vector) both using ranges::copy_if and also using vector constructor taking 2 iterators(by providing it with filter_view .begin() and .end()).
#include <algorithm>
#include <iostream>
#include <ranges>
#include <vector>
#include <utility>
#include <fmt/ranges.h>
const std::vector<int> vals{1,2,3,47,8472};
const auto filter_pred = [](const int i){return i%2==0;};
void fna(){
std::vector<int> result;
std::ranges::copy_if(vals, std::back_inserter(result), filter_pred);
std::cout << fmt::format("{}", result) << std::endl;
}
void fn1(){
auto filtered = vals | std::views::filter(filter_pred);
std::vector<int> result {filtered.begin(), filtered.end()};
std::cout << fmt::format("{}", result) << std::endl;
}
std::vector<int> fna_val(){
std::vector<int> result;
std::ranges::copy_if(vals, std::back_inserter(result), filter_pred);
return result;
}
std::vector<int> fn1_val(){
auto filtered = vals | std::views::filter(filter_pred);
return {filtered.begin(), filtered.end()};
}
int main(){
fna();
fn1();
std::cout << fmt::format("{}", fna_val()) << std::endl;
std::cout << fmt::format("{}", fn1_val()) << std::endl;
}
Is there a reason to prefer one over other? What I can think of:
- it is unlikely that either is more efficient, since I doubt that any real optimizations can be done since because _if in
copy_ifmeans destination size is unknown. - compile speed:
copy_ifcould be faster to compile since it does not require<ranges>header, but did not benchmark - const all the things:
copy_ifrequires mutable destination container, range construction does not - verbosity: when return type of function is known we do not need to spell out the
std::vectorin body,{}works. Would be nice if vector understood ranges so we would not have to use.begin().end(), but it does not.
valsisviews::iota(0) | views::take(5),fn1()will not work because thevectormust accept the iterator-pairs of same types.