ericniebler / range-v3

Range library for C++14/17/20, basis for C++20's std::ranges
Other
4.06k stars 437 forks source link

Using zip and filter produces copies? #1704

Closed Talkless closed 2 years ago

Talkless commented 2 years ago

Here's simple example (try https://godbolt.org/z/5nM9jr4Gb):

#include <range/v3/all.hpp>

#include <vector>
#include <cstdio>

struct Foo {

    Foo() noexcept { puts("Foo()"); };
    ~Foo() { puts("~Foo()"); }
    Foo(const Foo&) noexcept { puts("Foo(const Foo&)"); };
    Foo(Foo&&) noexcept { puts("Foo(Foo&&)"); };
    Foo& operator=(const Foo&) { puts ("Foo::operator=(const Foo&)"); return *this; }
    Foo& operator=(Foo&&) { puts ("Foo::operator=(Foo&&)"); return *this; }

    void bar() const { puts("woof!"); }
};

int main() {

    std::vector<int> iv{42};
    std::vector<Foo> fv{Foo{}};

    puts("done initializing");

    namespace r = ranges;
    namespace rv = ranges::views;
    for (const auto i: rv::zip(iv, fv) | rv::filter([](const auto& pair) {
        //typename decltype(pair)::typeof is;
        return pair.first == 42;
    }) | rv::values) {
        i.bar();
    }
}

Application prints:

Foo()
Foo(const Foo&)
~Foo()
done initializing
Foo(const Foo&)
woof!
~Foo()
~Foo()

So we see that filtering produce additional Foo copy during filtering (after initializing vector).

If we uncomment line //typename decltype(pair)::typeof is; we discover that pair's type is:

const std::pair<int, Foo>&

Is there a way to avoid Foo copies, i.e. make it a reference to const? Imagine if second zipped range is vector of vectors, and this behavior means copying of that vector on every filter()'ing?

brevzin commented 2 years ago

Is there a way to avoid Foo copies, i.e. make it a reference to const?

Yes, don't ask for copies here:

for (const auto i:
//         ~~~~^

You meant const auto&.

Talkless commented 2 years ago

Oh boy, no that was quite embarrassing... Thanks @brevzin , and sorry for the noise.