0

This is a follow-up to Debug assertion while iterating over composed views.

I replaced problematic transform_view with a loop and the debug assertion was gone. Then I added a filter and the problem is back:

#include <iostream>
#include <ranges>
#include <string>
#include <vector>


struct Token
{
    std::string value;
    bool used = false;
};

int main()
{
    auto tokens = std::vector{Token{"p1"}, Token{"p2"}, Token{"++"}, Token{"p3"}};

    auto view = tokens
        | std::views::drop_while([](auto const & token) { return token.used; })
        | std::views::take_while([](auto const & token) { return !token.used; })
        | std::views::filter([](auto const &) { return true; }); // without this filter it works fine

    auto strs = std::vector<std::string>();
    for (auto& elem : view)
    {
        elem.used = true;
        strs.push_back(elem.value);
    }

    for (auto const & str : strs)
    {
        std::cout << str << ", ";
    }

    return 0;
}

This time the error is (Visual Studio 2022, version 17.13.2, debug mode, /std:c++20 /MDd options): "Expression: Cannot increment filter_view iterator past end".

The filter's predicate does not depend on the pointed value, so I suppose the problem stems from its interaction with take_while. Is there a way to stay away from such pitfalls, as once again gcc and clang seem to compile and run the code just fine.

The usual goto, cppreference.com, does not provide any clue on what constrain, if any, my code violates.

7
  • This is Visual Studio 2022, version 17.13.2. Compiler version is 19.43.34808. I am always confused by these version numbers :-) Commented Mar 11 at 21:32
  • can you reproduce the error here godbolt.org/z/qhnM543En ? Please include the complete error message in the question. Commented Mar 11 at 22:15
  • 2
    The usual goto, cppreference.com, does not provide any clue on what constrain, if any, my code violates. You see? You see?!? goto IS harmful! Commented Mar 11 at 22:43
  • 2
    Doesn’t the answer to the previous question also apply to this one? The range-based for loop dereferences the iterator (modifies the element) before doing the ++. Commented Mar 12 at 3:15
  • @463035818_is_not_an_ai Yes, the example on Compiler Explorer fails when you pass /std:c++20 /MDd options and execute the code. It does not output anything to stdout and returns non-zero exit code. Commented Mar 12 at 7:30

1 Answer 1

4

It's the same error as before. You are changing what elements satisfy the take_while's predicate while iterating.

The constraint you are breaking is on InputIterator. ++r has a precondition that *r be dereferenceable. A past-the-end iterator is not dereferenceable. The modification in the body of the loop makes the current iterator (unnamed because it is a range-for loop) a past-the-end iterator.

Is there a way to stay away from such pitfalls?

Don't do modifications that will change how your views filter the range they are operating on, whilst iterating the resulting range.

gcc and clang seem to compile and run the code just fine

Calling a function in std while not meeting it's precondition is undefined behaviour. Doing what you expect is a valid symptom of undefined behaviour

Sign up to request clarification or add additional context in comments.

2 Comments

Yeah, undefined behaviour has bitten me in unexpected ways before, and here it does it again. A curious thing is why adding a dummy filter exposes the problem, so the debug machinery can find and report it, but it's another story.
@KrzysiekKarbowiak it's a debug assertion. To give the extra info, take_while doesn't need to check it's iterator you just dereferenced isn't past the end, it only needs to look at it's input. filter does need to check it isn't advancing past the end of it's input

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.