在C++中,是否允许删除list<Pointer>::unique中的对象?

4
我们有一些遗留代码,它返回指向堆分配对象的巨大大小列表(我们无法使用智能指针),我们将从列表中删除重复项,并从堆中删除它们。
现在,正如专家建议的那样,我想尝试std::list::unique(或forward_list::unique)而不是算法std::unique。
我在http://en.cppreference.com/w/cpp/container/list/unique中读到,在“unique”谓词中,我们不应更改对象,因此按照标准术语删除“将要被删除”的对象是否安全?
如果是这样,list::unique中的哪个对象应被视为重复项?在gnu实现中,“b”将被删除,但在http://www.cplusplus.com/reference/list/list/unique/中写道,在pred(i, i-1)中,i项将被删除,那么这种行为是否由标准指定?
在标准方面,这段代码是否正确,还是UB(未定义行为)?
List.sort( [] (const Val *a, const Val *b) { 
    return *a < *b;
});

List.unique([] (const Val *a, const Val *b) {
    if (*a == *b) {
        delete b;  // (1) working in gcc 4.6
        // or (2) delete a (elsewhere)? 
        return true;
    }

    return false;
}) ;

更新 #1

Mike的解释是最有帮助的,但现在我们正在使用这样的解决方案:

struct RawPtrEq {
    bool operator()(const Val a, const Val b) { return *a == *b;}
};

auto it = adjacent_find( begin(List), end(List), RawPtrEq() );

while(it != end(li)) {
    delete *it;
    it = List.erase(it);

    it = adjacent_find( it, end(List), RawPtrEq() );
}
1个回答

1
没有保证这是明确定义的。unique 没有完全指定,不能保证这是最后一次将 b 传递给谓词,因此删除的指针可能会在以后再次使用。
我很惊讶它对你起作用了,因为规范是要求 b 是两个元素中的第一个,如果其中任何一个被删除,它将被保留。
我建议存储对象本身或 unique_ptr(如果您确实需要它们是动态的),以便它们在删除时始终自动销毁。我不知道为什么你说“我们不能使用智能指针”; 它们比绕过遗留代码更有意义。

2
我不确定这是否定义良好。标准规定谓词的第一个参数将被擦除(比较是 pred(*i, *(i - 1))),因此如果使用 delete b,我们将删除下一个比较中将再次引用的对象。 - Wojtek Surowka
@WojtekSurowka:说得好,我没有仔细考虑。我想的是remove_if,它只会对每个元素应用一次谓词。 - Mike Seymour
由于@MikeSeymour的解释(以及一点点谷歌搜索),我现在认为使用unique_ptr<T>这个习惯用法是最合理的,但在遗留代码所有者处理接口更改之前,我将使用迭代器遍历列表并使用adjacent_find删除重复项的解决方案。 - Alex Karev

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接