gerion
gerion

Reputation: 365

Range based for loop: Iterate over vector extended with one element

I want to achieve more less the equivalent to this Python code in C++ (but more memory efficient):

a = [1, 5, 3]
additional = 6
for elem in [additional] + a:
    print(elem) # prints: 6 1 5 3

# alternative without creating the additional vector:
import itertools
for elem in itertools.chain([additional], a):
    print(elem)

The only way that I know to do this in C++ is:

#include <iostream>
#include <vector>

int main() {
        std::vector<int> a = {1, 5, 3};
        int additional = 6;

        for (int i = 0; i < a.size() + 1; ++i) {
                int cur_elem;
                if (i == 0) {
                        cur_elem = additional;
                } else {
                        cur_elem = a[i-1];
                }
                std::cout << cur_elem << std::endl;
        }
}

Is there a way to do this with a range based for loop? I found the Boost join operator but it seems to use only iterables, so I need to create an extra vector (like in the Python example).

Ideally, the iteration would be without creating the joined object in the memory and with algorithms of the standard library.

Upvotes: 1

Views: 551

Answers (1)

Not a real meerkat
Not a real meerkat

Reputation: 5729

It can be done using the upcoming ranges feature.

Here's an example using Eric Niebler's range-v3 library:

#include <iostream>
#include <vector>

#include <range/v3/view/concat.hpp>
#include <range/v3/view/single.hpp>

int main() {
    std::vector<int> a = {1, 5, 3};
    int additional = 6;
    for (auto i : ranges::concat_view(ranges::single_view{additional}, a)) {
        std::cout << i;
    }
}

See it live!

by using views, all iterator operations are lazy, and no extra memory is used (e.g.: no extra vectors/arrays are created)

Or, without the for loop:

ranges::copy(ranges::concat_view(ranges::single_view{additional}, a), ranges::make_ostream_joiner(std::cout, ","));

See it live!

(Honestly, I like the for version better, though)

Standard-compliant solution

There's a small issue with the solution above: concat_view did not make it into C++20. If you want a strictly compliant solution, you may want to create your own version, or use join_view instead:

#include <iostream>
#include <vector>

#include <ranges>

int main() {
    std::vector<int> a = {1, 5, 3};
    int additional = 6;

    std::vector v{{additional}, a};

    for(int i : std::ranges::join_view{v}) {
        std::cout << i;
    }
}

Upvotes: 6

Related Questions