2

For some reason, I get different result when comparing the return value from vector::erase inside a if statement or if I store the value first and then compare. But it seems to only happen with g++.

This is built on Ubuntu 20.04 AMD64 using g++, libstdc++ and with -std=c++14 flag. g++ -std=c++14 foo.cpp && ./a.out

This will return false

#include <vector>
#include <iostream>
#include <algorithm>

int main()
{
    std::vector<int> v{0, 1, 8, 3, 8, 5, 8, 7,8, 9};
    int thing_id{9};

    std::vector<int>::iterator const cit{
        std::remove_if(v.begin(),
                        v.end(),
                        [thing_id](int const& thing) -> bool {
                            return thing == thing_id;
                        })};

    if (v.erase(cit, v.cend()) == v.cend()) {
        std::cout << "true\n";
        return true;
    }
    else {
        std::cout << "false\n";
        return false;
    }
}

This will return true

#include <vector>
#include <iostream>
#include <algorithm>

int main()
{
    std::vector<int> v{0, 1, 8, 3, 8, 5, 8, 7,8, 9};
    int thing_id{9};

    std::vector<int>::iterator const cit{
        std::remove_if(v.begin(),
                        v.end(),
                        [thing_id](int const& thing) -> bool {
                            return thing == thing_id;
                        })};

    auto const prev_end = v.erase(cit, v.cend());
    if (prev_end == v.cend()) {
        std::cout << "true\n";
        return true;
    }
    else {
        std::cout << "false\n";
        return false;
    }
}

When building for QNX ARM64 with QCC and libc++, both snippets will return true.

Why is this? Should not the comparisons be deterministic either or? Can someone please explain to me what is going on here?

2
  • 1
    On a side note: since your vector elements are simple ints and not objects, You can simplify your use of std::remove_if() by using std::remove() instead, you don't need the lambda at all, eg: std::remove(v.begin(), v.end(), thing_id); Commented Sep 12, 2023 at 1:11
  • Yes, thank you. I'm aware. But the example is a simplified version of a more complex code base where we have objects being compared. I just wanted to keep it as close as possible to the original code without having to add a struct or class in the example. :) Commented Sep 12, 2023 at 6:53

1 Answer 1

5

When std::vector::erase succeeds, it invalidates iterators (and references) to the elements at or after the point of the erasure. Thus the end() iterator gets invalidated as well. In other words, after the erasure will succeed, the vector will have the new cend().

This is important because the order of evaluation of the comparison operator is unspecified, see this answer. In other words, given the expression v.erase(cit, v.cend()) == v.cend(), the compiler is free to decide to evaluate the rightmost v.cend() first, remember it, and only then evaluate the return value of v.erase(cit, v.cend()). In such a case, it would be comparing the newly returned past-the-end iterator value to the old v.cend() value (i.e., before it got invalidated by the erasure).

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

5 Comments

Ah. I see. Yes, that makes sense. So I guess there might be a subtle difference between the implementation in libc++ vs libstdc++? Or in how comparison operator works when compiled with qcc?
When changing the one line comparison to if (v.cend() == v.erase(cit, v.cend())) it also started returning true. But that would still not be guaranteed to always work like that?
I doubt it depends on the library implementation. I think more likely some compilers just tend to reorder certain invocations for whatever "optimization" reasons they might detect. Don't rely on the order of evaluation in cases where the C++ standard does not specify it. Sometimes it is explicitly specified, for example, the so-called short-circuit evaluation of built-in && and || is guaranteed, but in general, it is not.
BTW, the c++14 tag should only be used along with the more general c++ tag (so the people interested in C++ and watching the corresponding general tag would find your question).
Mkay... Changed my tags. 👍

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.