std::remove_if不能移除所有的项。

5

我希望能够清除输入中的所有非唯一值。删除重复项后的子集应与输入相同。某些字符仍然留在输入中,未能将所有字符删除。看起来谓词内部的std::map大小也在减少。

我正在使用的std::remove_if()谓词是:

template<class T>
class RemovePredicate {

    public:

        RemovePredicate() : m_oldsize(0) {}

        bool operator()(const T& value)
        {
            //
            bool retval;
            m_uniques[value] ='a'; // 'a' could be any value
            cout << m_uniques.size() << endl;
            retval = m_uniques.size() == m_oldsize;
            m_oldsize = m_uniques.size();
            return retval;
        }

    private:

        std::map<T, char>   m_uniques;
        unsigned            m_oldsize;

};

我设计了谓词,使得当我看到大小增加时,就没有遇到输入。因此,当大小不同时,我不会删除输入。当大小保持不变时,我又遇到了那个输入值,然后我会将其删除。

测试代码如下:

template<class T>
void print(T iterable)
{
    for (auto c : iterable)
        cout << c;
    cout << endl;
}


int main(int argc, char** argv){
    if (argc != 2)
        return 1;

    char * str= argv[1];

    vector <char> charvec (str, str + strlen(str));
    print(charvec);

    auto itend = std::remove_if(charvec.begin(),
                                charvec.end(),
                                RemovePredicate<char>()
                                );
    print(charvec);
    // apply erase remove idiom
    charvec.erase(itend, charvec.end()); 
    print(charvec);
    return 0;
}

一个示例输入是:

./remove_duplicates deadbeef

输出结果为

deabef

但是你可以看到输出结果中仍然有双重 'e'。 不过好消息是原始顺序得以保留。

我做错了什么?


1
不是针对你的问题的答案,但我会在RemovePredicate中使用std::set<T>而不是std::map。您只需检查set<T>::insert的返回值即可查看要插入的值是否已存在于集合中。 - Steve Lorimer
@Steve 谢谢,这是一个受欢迎的补充。 - hetepeperfan
1
@SteveLorimer:或者甚至是unordered_set<T> - Ben Voigt
对于这种情况,GCC的remove_if本质上首先调用find_if查找要删除的第一个元素,传递谓词的副本,然后从那里开始进行。此外,remove_if不能保证按顺序处理输入范围。 - T.C.
1个回答

5

不能保证每次调用谓词时都会在同一个函数对象的副本上进行。

您需要安排副本共享单个 map(或 setunordered_set),例如通过在更广泛的范围内声明 map 并保留引用,或者使用 shared_ptr(因此函数对象作为一组仍然拥有它)。


1
或者传递一个 reference_wrapper<RemovePredicate> - T.C.
这对我来说很有意义。在C++标准库的许多谓词中,是否存在不能保证成员在集合中的多次调用中仍然有效? - hetepeperfan
2
@hetepeperfan:所有这些都是如此。C++标准在§25.1/10中明确指出:“除非另有规定,以函数对象为参数的算法允许自由复制这些函数对象。对于那些对象标识很重要的程序员,应考虑使用一个指向非复制实现对象的包装类,例如reference_wrapper<T>(20.9.4),或一些等效的解决方案。” - Christian Hackl

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