STL std::remove_if 编译器错误

5

我无法让 std::remove_if 编译通过。你可以看到,我选择了一种替代的手动方法,它能正常工作。编译器错误在代码清单底部。

如果能得到任何帮助将不胜感激。

谢谢, Tom

#include <iostream>
#include <fstream>
#include <set>
#include <algorithm>
#include <string>

//
// Find the largest compound word composed
// of sub-words from a list.
//
// - read list from file. 
//
// Psuedo Code:
//
// 1. Read Next Word from File.
// 2. Search in list for word formed from word.
// 3. if Found in List
// 4.   if Found Compound is longer then Current Compound
// 5.     Replace
// 6.     Remove all strings less then Current Compound Length 
// 7. 
//

typedef std::set<std::string> StrSet;
typedef StrSet::iterator StrSetIter;


struct if_substr
{
    std::string m_word;
public:
    if_substr(const std::string& w) : m_word(w) { }

    bool operator() (const std::string& str) const
    {
    std::size_t f = m_word.find(str);
    return (std::string::npos!=f && m_word.length()>str.length());
    }

};

struct if_remove
{
    std::string m_word;
public:
    if_remove(const std::string& w) : m_word(w) { }

    bool operator() (std::string str) const
    {
        return m_word.length()>str.length();
    }

};


class FindLongestCompound
{

    std::ifstream m_file;

    StrSet m_words;
    std::string m_current;

public:
    FindLongestCompound(std::string filename)
    {
    m_file.open(filename, std::ifstream::in); 

    if (!m_file)
        throw std::runtime_error("Failed to open file"+filename);
    }


    void Start(void)
    {
    std::string nextWord;
    while(m_file >> nextWord)
    {

        std::cout << "read word: " << nextWord << std::endl;
        if_substr ifSubstr(nextWord);
        StrSetIter srchItem = std::find_if(std::begin(m_words), std::end(m_words),ifSubstr);
        if (srchItem != m_words.end())
        {
        m_current = nextWord; 
        std::cout << "new current: " << m_current << std::endl;


        if_remove ifRemove(m_current);
        std::remove_if(m_words.begin(), m_words.end(),ifRemove);

        #if 0
        StrSetIter j = m_words.begin();
        do 
        {
            if (j->length() < m_current.length()) 
            j = m_words.erase(j);
            else 
            j++;

        } while (j != m_words.end());
        #endif
        } 
        std::cout << "insert next word: " << nextWord << std::endl;
        m_words.insert(nextWord);
    } 
    }

    std::string result() { return m_current; } 

};


int main(int argc, char *argv[])
{
    if (argc<2)
    {
    std::cout << "Please provide filename" << std::endl;
    return -1;
    }

    FindLongestCompound flc(argv[1]);

    flc.Start();

    std::cout << "result: " << flc.result() << std::endl;
} 
--- errors ---
In file included from stackoverflow.C:2:
In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/iostream:38:
In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/ios:216:
In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/__locale:15:
In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/string:439:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/algorithm:2148:26:
error: no viable overloaded '='
                    *__first = _VSTD::move(*__i);
                    ~~~~~~~~ ^ ~~~~~~~~~~~~~~~~~
    stackoverflow.C:91:8: note: in instantiation of function template specialization
'std::__1::remove_if<std::__1::__tree_const_iterator<std::__1::basic_string<char>,
std::__1::__tree_node<std::__1::basic_string<char>, void *> *, long>,
if_remove>' requested here
                    std::remove_if(m_words.begin(), m_words.end(),ifRemove);
                         ^
    /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/string:1415:19:
note: candidate function not viable: 'this' argument has type 'const
value_type' (aka 'const std::__1::basic_string<char>'), but method is
not marked const
        basic_string& operator=(const basic_string& __str);
                      ^
    /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/string:1422:45:
note: candidate function not viable: 'this' argument has type 'const
value_type' (aka 'const std::__1::basic_string<char>'), but method is
not marked const
        _LIBCPP_INLINE_VISIBILITY basic_string& operator=(const value_type* __s) {return assign(__s);}
                                                ^
    /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/string:1423:19:
note: candidate function not viable: 'this' argument has type 'const
value_type' (aka 'const std::__1::basic_string<char>'), but method is
not marked const
        basic_string& operator=(value_type __c);
                      ^
    1 error generated.

2
缩小你的问题范围。 - Kerrek SB
1
请尝试创建一个最小化、完整和可验证的示例 - Some programmer dude
1
另外,如果你正在使用C++11进行编程,为什么要使用函数对象而不是lambda表达式呢? - Some programmer dude
1
谢谢您的评论,是的,学习STL很痛苦,但这里的人们非常棒。 - Tom Orsi
@TomOrsi:"STL"式编程有很多初始时隐藏的优点。例如,你在这里遇到的erase-remove惯用语可能会让你觉得繁琐,但是当你考虑到算法总是需要两个迭代器,并且第二个迭代器并不总是容器的end()时,“已删除”的元素不一定要被删除更加合理。 - Christian Hackl
1个回答

7
问题并不出在你的函数对象上,而是你使用的容器类型。你不能在一个std::set(这里指的是你的StrSet)中使用std::remove_if。
简单地说,std::remove_if 并不会真正删除任何元素,而是只是改变元素的顺序,使得“被删除”的元素只出现在某个点之后。只有erase才能真正将它们删除。
然而,std::set有一个定义好的元素顺序,无法通过任何操作来绕过它。假设你有以下按字典序排序的字符串集合:
{ "aaa", "bbb", "ccc", "ddd", "eee" }

现在你试着应用一个函数对象来使用std::remove_if函数移除所有仅由元音字母组成的字符串。你将得到以下结果:

{ "bbb", "ccc", "ddd", "aaa", "eee" }
                     ^
                     |
        "removed" elements start here

这适用于例如 std::vector,但不适用于 std::set,因为这会导致一个元素无序的set("已删除"的元素仍然是集合的一部分!)。所以会得到编译错误。

为了实现您期望的目标,请在循环中使用 std::set::erase。它将能够正常工作,因为只有指向被删除元素的迭代器无效,所以 end() 仍然有效。

if_remove ifRemove(m_current);
for (StrSet::iterator set_iter = m_words.begin(); set_iter != m_words.end(); )
{
    if (ifRemove(*set_iter))
    {
        set_iter = m_words.erase(set_iter);
    }
    else
    {
        ++set_iter;
    }
}

在C++11中,你可以使用auto代替StrSet::iterator

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