admin管理员组

文章数量:1123924

I have the following test program:

#include <vector>
#include <print>
#include <ranges>

int main() {
    const std::vector<int> input = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

    auto output = input
    | std::views::filter([](const int n) { std::print("{} ", n); return n % 3 == 0; })
    | std::views::transform([](const int n) {return n * n; });

    const std::vector<int> vector = std::ranges::to<std::vector>( output );

    std::println("\ninput size: {} output size: {}", input.size(), vector.size() );
}

which, when compiled in Xcode 16.2 with c++23 mode, outputs this:

0 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
input size: 11 output size: 4

I'm confused by this. Why is each item evaluated twice when being converted to std::vector?

When I try a simple for cycle to print the elements, each item is only evaluated once:

#include <vector>
#include <print>
#include <ranges>

int main() {
    const std::vector<int> input = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

    auto output = input
    | std::views::filter([](const int n) { std::print("{} ", n); return n % 3 == 0; })
    | std::views::transform([](const int n) {return n * n; });

    int sum = 0;
    for ( int i : output ) sum += i;

    std::println("\nsum: {}", sum);
}

outputs:

0 1 2 3 4 5 6 7 8 9 10 
sum: 126

The first program seems inefficient and makes me reconsider if I should start using ranges... is there a better way to write it?

I have the following test program:

#include <vector>
#include <print>
#include <ranges>

int main() {
    const std::vector<int> input = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

    auto output = input
    | std::views::filter([](const int n) { std::print("{} ", n); return n % 3 == 0; })
    | std::views::transform([](const int n) {return n * n; });

    const std::vector<int> vector = std::ranges::to<std::vector>( output );

    std::println("\ninput size: {} output size: {}", input.size(), vector.size() );
}

which, when compiled in Xcode 16.2 with c++23 mode, outputs this:

0 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
input size: 11 output size: 4

I'm confused by this. Why is each item evaluated twice when being converted to std::vector?

When I try a simple for cycle to print the elements, each item is only evaluated once:

#include <vector>
#include <print>
#include <ranges>

int main() {
    const std::vector<int> input = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

    auto output = input
    | std::views::filter([](const int n) { std::print("{} ", n); return n % 3 == 0; })
    | std::views::transform([](const int n) {return n * n; });

    int sum = 0;
    for ( int i : output ) sum += i;

    std::println("\nsum: {}", sum);
}

outputs:

0 1 2 3 4 5 6 7 8 9 10 
sum: 126

The first program seems inefficient and makes me reconsider if I should start using ranges... is there a better way to write it?

Share Improve this question edited yesterday Ted Lyngmo 117k7 gold badges80 silver badges130 bronze badges asked yesterday Tomas AndrleTomas Andrle 13.3k15 gold badges79 silver badges94 bronze badges 6
  • 1 Presumably, to only allocate once for the vector, you would need to know the size of the range, which would require evaluating it completely. Then, the second evaluation is actually populating the vector. The alternative is to grow the vector as you evaluate the range. Your second example would be more fair if you tried to populate a vector as well, instead of simply summing the elements. – François Andrieux Commented yesterday
  • Can you share the compiler version and flags used? I don't have Xcode. When I try to reproduce the problem, I only ever get a single iteration of the range. – François Andrieux Commented yesterday
  • 1 @FrançoisAndrieux gcc (trunk) and clang (trunk) and MSVC does it twice: godbolt.org/z/xM716e3ab. gcc 14.2 and clang 19.1.0 does it one time only. Seems to be a change in libc++. In libstdc++ it's done twice with clang 19.1.0 too. – Ted Lyngmo Commented yesterday
  • My compiler was Apple clang version 16.0.0 (clang-1600.0.26.6) – Tomas Andrle Commented yesterday
  • 1 @TomasAndrle Yes. Sounds hard when using to(). – Ted Lyngmo Commented yesterday
 |  Show 1 more comment

1 Answer 1

Reset to default 6

std::ranges::to<std::vector>( output ) computes the length of output before initializing the elements. That is, there are two iterations:

  1. The first iteration is used to determine the length of output (which is also the size of the resulting std::vector).
  2. The second iteration is used to initialize the elements of the resulting std::vector.

This approach is usually more efficient than a single iteration, because it avoids reallocation during vector construction.

本文标签: cWhy is this stdview getting evaluated twiceStack Overflow