How do I write a range pipeline that uses temporary containers?

前端 未结 6 2410
予麋鹿
予麋鹿 2020-11-30 02:09

I have a third-party function with this signature:

std::vector f(T t);

I also have an existing potentially infinite range (of the

6条回答
  •  北海茫月
    2020-11-30 02:43

    range-v3 forbids views over temporary containers to help us avoid the creation of dangling iterators. Your example demonstrates exactly why this rule is necessary in view compositions:

    auto rng = src | view::transform(f) | view::join;
    

    If view::join were to store the begin and end iterators of the temporary vector returned by f, they would be invalidated before ever being used.

    "That's all great, Casey, but why don't range-v3 views store temporary ranges like this internally?"

    Because performance. Much like how the performance of the STL algorithms is predicated on the requirement that iterator operations are O(1), the performance of view compositions is predicated on the requirement that view operations are O(1). If views were to store temporary ranges in internal containers "behind your back" then the complexity of view operations - and hence compositions - would become unpredictable.

    "Ok, fine. Given that I understand all of this wonderful design, how do I MAKE THIS WORK?!??"

    Since the view composition won't store the temporary ranges for you, you need to dump them into some kind of storage yourself, e.g.:

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    using T = int;
    
    std::vector f(T t) { return std::vector(2, t); }
    
    int main() {
        std::vector buffer;
        auto store = [&buffer](std::vector data) -> std::vector& {
            return buffer = std::move(data);
        };
    
        auto rng = ranges::view::ints
            | ranges::view::transform(ranges::compose(store, f))
            | ranges::view::join;
    
        unsigned count = 0;
        RANGES_FOR(auto&& i, rng) {
            if (count) std::cout << ' ';
            else std::cout << '\n';
            count = (count + 1) % 8;
            std::cout << i << ',';
        }
    }
    

    Note that the correctness of this approach depends on the fact that view::join is an input range and therefore single-pass.

    "This isn't novice-friendly. Heck, it isn't expert-friendly. Why isn't there some kind of support for 'temporary storage materialization™' in range-v3?"

    Because we haven't gotten around to it - patches welcome ;)

提交回复
热议问题