c++stdstd-rangesc++23

Invalid use of std::ranges / std::filter


Below is a minimal example of an attempt to use the std::ranges library to look at a map<int,...> and return only the first elements such that the int is contiguous starting at one. However, the code crashes while looping through the names at for (auto& name : names) giving ITERATOR LIST CORRUPTED (msvc 2019). I assume i somehow have a reference to a temporary, but I cannot figure out where.

Can anyone help me identify the mistake?

NOTE: I know this is solution not great - it will keep checking after an element fails instead of ending early, and the range only works once, c++23 can use views::zip() with an unbounded views::iota and views::take_while instead.

#include <iostream>
#include <map>
#include <ranges>

struct NameHolder
{
    auto contiguous_indexed_names()
    {
        return grouped_name_sequences | std::views::transform([](auto& group_names) 
        {
            auto sequential_names = group_names.second | std::views::filter([expected = 1](auto& idx_name) mutable
            {
                return idx_name.first == expected++;
            }) | std::views::values;

            return std::forward_as_tuple(group_names.first, sequential_names);
            // solution: return std::tuple<decltype(group_names.first)&, decltype(sequential_names)>(group_names.first, sequential_names);

        });
    }

    void add(const std::string& group, int sequence, std::string name)
    {
        grouped_name_sequences[group].emplace(sequence, std::move(name));
    }

    std::map<std::string, std::map<int, std::string>> grouped_name_sequences;
};

int main()
{
    NameHolder grouped_names;
    grouped_names.add("Group1", 1, "A");
    grouped_names.add("Group1", 2, "B");
    grouped_names.add("Group1", 4, "D");
    for (auto [group, names] : grouped_names.contiguous_indexed_names())
    {
        for (auto& name : names)
        {
            // expect only "A", "B"
            std::cout << name << "\n";
        }
    }
    return 0;
}

Solution

  • You are returning a reference to a local variable.

    auto sequential_names = group_names.second | std::views::filter([expected = 1](auto& idx_name) mutable
    {
        return idx_name.first == expected++;
    }) | std::views::values;
    
    return std::forward_as_tuple(group_names.first, sequential_names);
    

    Above, sequential_names is a view that you have created on the stack.

    std::forward_as_tuple produces a reference to this stack variable, which you are returning.

    Accessing this dangling reference is Undefined Behavior.