与 if 进行比较时与与存储值进行比较时的 std::vector擦除结果不同

mat*_*son 2 c++ libstdc++ comparison-operators erase-remove-idiom qcc

vector::erase由于某种原因,当比较语句内部的返回值if或者先存储值然后比较时,我会得到不同的结果。但这似乎只发生在 g++ 中。

这是使用 g++、libstdc++ 和-std=c++14flag 在 Ubuntu 20.04 AMD64 上构建的。 g++ -std=c++14 foo.cpp && ./a.out

这将返回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;
    }
}

Run Code Online (Sandbox Code Playgroud)

这将返回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;
    }
}

Run Code Online (Sandbox Code Playgroud)

当使用 QCC 和 libc++ 构建 QNX ARM64 时,两个片段都将返回true.

为什么是这样?比较不应该是确定性的吗?有人可以向我解释一下这是怎么回事吗?

hea*_*run 5

std::vector::erase成功时,它会使擦除点或擦除点之后的元素的迭代器(和引用)无效。因此end()迭代器也会失效。换句话说,擦除成功后,向量将拥有新的cend().

这很重要,因为比较运算符的求值顺序未指定,请参阅此答案。换句话说,给定表达式v.erase(cit, v.cend()) == v.cend(),编译器可以自由地决定首先计算最右边的值v.cend(),记住它,然后才计算 的返回值v.erase(cit, v.cend())。在这种情况下,它将把新返回的尾后迭代器值与旧v.cend()值进行比较(即,在它因擦除而失效之前)。

  • 我怀疑这取决于库的实现。我认为更有可能的是,某些编译器只是倾向于出于它们可能检测到的任何“优化”原因来重新排序某些调用。在 C++ 标准未指定的情况下,不要依赖计算顺序。有时会明确指定,例如保证内置`&amp;&amp;`和`||`的所谓_短路评估_,但一般情况下,不是这样。 (2认同)