c++stdc++20c++23

Should std::get<T&>(tuple) work for zip_view's output?


In short: std::get<T&>('tuple-like') does not work when using ranges library in MSVC, and I wonder what is expected according to the C++ standard.

I cannot get the code below to compile correctly on MSVC 19.40. It does work with both gcc and clang, though, and I am yet to find information that speaks in favour of MSVC's behaviour.

sample code

#include <tuple>
#include <ranges>
#include <algorithm>

int main() {
    using namespace std::views;

    char foo{};

    std::ranges::for_each(zip(iota(0, 2) | transform([&](int i) -> char& { return foo; })),
                          [] (auto t) { auto bar = std::get<char&>(t); });

    return 0;
}

error message

error C2039: '_Ttype': is not a member of 'std::_Tuple_element<_Ty,std::tuple<>>'
        with
        [
            _Ty=char &
        ]

Should I expect std::get to work here, or am I missing something?


Solution

  • The issue seems to be related to using

    std::ranges::for_each(..., [] (auto t) { ... });
    

    i.e. with auto parameter. Simply replacing auto with std::tuple<char&> works. I am still unsure whether this behaviour is correct, but it does compile with all three compilers.

    To make sure no unwanted tuple conversion was applied (i.e. std::tuple<char> <-> std::tuple<char&>), I replaced char foo{}; from the question with a no-copy-no-move structure

    struct no_copy_move {
        no_copy_move() = default;
        no_copy_move(no_copy_move const&) = delete;
        no_copy_move(no_copy_move&&) = delete;
    };
    no_copy_move foo{};
    

    and then changed the lambdas

    [&](int i) -> char& { return foo; }
    [] (auto t) { auto bar = std::get<char&>(t); }
    

    to

    [&](int i) -> no_copy_move& { return foo; }
    [] (std::tuple<no_copy_move&> t) { auto& bar = std::get<no_copy_move&>(t); }
    

    which also compiles.

    TLDR I do not know if std::get should work with auto parameter or not, but changing it to std::tuple<char&> works.