Search code examples
c++c++20std-ranges

Underlying Reason for Why ranges::filter_view is Modifiable but ranges::transform_view is not


I've been exploring C++20 ranges and views and used the cppinsights.io website to dig into the details. I noticed something interesting about how filter_view and transform_view behave differently when trying to modify the elements in a range. Now if you paste the code below into the cppinsights.io website, you'll notice that the type of iterators (__begin1, __end1) causes different modification behavior and the reason why transform_view is read-only. Is my observation correct? Can someone explain it?"

//For transform_view:
        
#include <algorithm>
#include <iostream>
#include <list>
#include <ranges>
#include <vector>

int main() {
    std::vector<int> numbers = { 1, 2, 3, 4, 5 };

    auto transformedView = numbers | std::views::transform([](int n) { return n * 2; });

    for (auto&& value : transformedView) {
        value += 1; // won't modify
    }

    for (const auto& num : numbers) {
        std::cout << num << " ";
    }
    std::cout << '\n';

    return 0;
}

//for filter

#include <algorithm>
#include <iostream>
#include <list>
#include <ranges>
#include <vector>

int main() {
    std::vector<int> numbers = { 1, 2, 3, 4, 5 };

    auto filterView = numbers | std::views::filter([](int n) { return n%2== 0; });

    for (auto&& value : filterView) {
        value += 1;  //  modify
    }


    for (const auto& num : numbers) {
        std::cout << num << " ";
    }
    std::cout << '\n';

    return 0;
}

https://cppinsights.io/ https://cppinsights.io/


Solution

  • A transform view returns the result of a function. In your case, you are returning the result of n * 2. This is prvalue int, with no association to the ints stored in numbers. Of course modifying this has no effect on numbers.

    If you have a transform that returns a mutable reference, then you can modify through it.

    struct number {
        number(int value): value(value) {}
        int value;
    };
    
    int main() {
        std::vector<number> numbers = { 1, 2, 3, 4, 5 };
    
        auto transformedView = numbers | std::views::transform([](number& n) -> int& { return n.value; });
    
        for (auto&& value : transformedView) {
            value += 1; // does modify
        }
    
        for (const auto& num : numbers) {
            std::cout << num.value << " ";
        }
        std::cout << '\n';
    
        return 0;
    }