【问题标题】:Weird Behaviour of std::uniquestd::unique 的奇怪行为
【发布时间】:2018-10-04 00:14:33
【问题描述】:

我通过 std::algorithm 学习,发现了独特之处,但在我自己实践之后,我发现输出不是我所期望的,或者我应该说不是文档声称的那样:

从范围 [first, last) 中的每个连续等效元素组中删除除第一个元素之外的所有元素,并为范围的新逻辑结束返回一个过去的迭代器。

vector<int> val = { 5,3,3,3,4};
unique(val.begin(), val.end());
for (auto it = val.begin(); it != val.end(); ++it)
    cout << *it << " ";

输出:5 3 4 3 4。它不应该返回:5 3 4 3 3。

我认为它需要先排序,所以我这样做:

vector<int> val = { 5,3,3,3,4};
sort(val.begin(), val.end());
unique(val.begin(), val.end());
for (auto it = val.begin(); it != val.end(); ++it)
    cout << *it << " ";

输出为:3 4 5 4 5。

我发现这很奇怪。你们知道为什么吗?

【问题讨论】:

    标签: c++ unique std


    【解决方案1】:

    cppreference on std::unique 为您解答:

    保留元素的相对顺序,容器的物理大小不变。指向新的逻辑结束和范围的物理结束之间的元素的迭代器仍然是可解引用的,但元素本身具有未指定的值。调用 unique 之后通常会调用容器的擦除方法,该方法会擦除未指定的值并减小容器的物理大小以匹配其新的逻辑大小。

    因此,要真正擦除元素,您还应该使用vector::erase,链接页面提供了一个示例:

    std::vector<int> v { 5, 3, 3, 3, 4 };
    auto last = std::unique(v.begin(), v.end());
    v.erase(last, v.end());
    for (auto i : v)
        cout << i << "\n";
    

    现在是输出

    5
    3
    4

    【讨论】:

    • 我明白了,所以使用唯一的唯一方法是擦除,删除重复值,谢谢。
    【解决方案2】:

    注意 std::unique

     "returns a past-the-end iterator for the new logical end of the range"
    

    所以要查看新的范围试试:

    auto new_end = std::unique(val.begin(), val.end());
    for (auto it = val.begin(); it != new_end; ++it)
       cout << *it << " ";
    

    【讨论】:

      【解决方案3】:

      您从std::unique 获得的唯一保证是您所说的:从开始迭代器到返回的结束后迭代器的序列是指定的。没有指定容器中的备用值会发生什么,从返回的结束迭代器开始并一直持续到原始序列结束迭代器。

      std::unique 的合同中还有一些其他小细节/保证/whatnots,但它们在这里并不密切。最重要的是,您可以从 std::unique 中得到的唯一期望是从开始迭代器到返回的新的过去结束迭代器的序列,并且未指定原始容器中的任何剩余值。

      您的观察结果与您的实现的std::unique 使用移动语义一致,对于普通的ints,它等同于复制语义。因此,原始迭代器中的剩余值是曾经存在的值的副本。这是std::unique 的典型实现,但不是必需的。

      【讨论】:

        猜你喜欢
        • 2011-03-06
        • 2020-06-11
        • 2013-03-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多