I am unable to get std::remove_if to compile as you can see I chose an alternative hand crank method that works fine, the compiler errors are at the bottom of the listing after the code.
Any help would be much appreciated.
Thanks, 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.
The problem is not with your functor but with the container type you're using. You cannot use std::remove_if
with a std::set
(your StrSet
in this case).
Put simply, std::remove_if
does not remove anything but just changes the order of elements such that the "removed" elements only appear after a certain point. Only erase
really eliminates them.
A std::set
, however, has a defined element order which cannot be bypassed by any operation. Let's say you have the following set of lexicographically ordered strings:
{ "aaa", "bbb", "ccc", "ddd", "eee" }
Now you try to apply std::remove_if
with a functor that removes all vowel-only strings. You would end up with this:
{ "bbb", "ccc", "ddd", "aaa", "eee" }
^
|
"removed" elements start here
This works with e.g. std::vector
but not with std::set
because it would result in a set whose elements are no longer ordered correctly (the "removed" elements are still part of the set!). So you get a compilation error.
In order to achieve your desired goal, use std::set::erase
in a loop. It will work fine because only iterators to the erased element are invalidated, so end()
remains valid.
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;
}
}
In C++11, you could use auto
instead of StrSet::iterator
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With