C++ Logo

std-proposals

Advanced search

[std-proposals] Evaluation order of `std::ranges::tuple-transform`'s arguments

From: Srik Wanar <srik_wanar_at_[hidden]>
Date: Tue, 17 Jun 2025 07:55:37 +0800 (CST)
It is intuitively assumed that zip_view steps iterators in some consistent order. The following program demonstrates a typical use case: https://godbolt.org/z/7M3bx9o3d
#include<ranges>
#include<sstream>
#include<print>


int main()
{
    namespace views = std::views;
    auto input = std::istringstream("1 2 3 4 5 6");
    for (auto [i, j] : views::zip(views::istream<int>(input), views::istream<int>(input)))
        std::print("({}, {}), ", i, j);
    std::println();


    auto input2 = std::istringstream("a 1 b 2 c 3");
    for (auto [i, j] : views::zip(views::istream<char>(input2), views::istream<int>(input2)))
        std::print("({}, {}), ", i, j);
}



However, g++ produces unexpected results:

First loop outputs (2, 1), (3, 4), (5, 6)

Second loop outputs nothing

The istream_view bound to a stream sets failbit on invalid input (e.g., reading 'a' as int), halting subsequent operations.


While the evaluation order of arguments of a function call is unspecified, leading to non-portable behavior,
enforcing an order that is consistent between
tuple-transform and tuple-for-each does no hurt.


Microsoft STL already implements tuple-transform by std::tuple{...}instead of std::tuple(...).


Maybe an LWG issue is in needed to specify left-to-right evaluation order for iterator constructions (begin()/end()) and iterator advancements (operator++) in zip_view and related adaptors.

Received on 2025-06-16 23:55:49