如何为vector<string>的remove_if编写匹配指定单词的一元谓词?
remove_if to Remove Elements Containing a Specific Word Let's break down the problems in your code and fix them step by step:
First: Why Your Current Code Fails
std::remove_if expects a unary predicate — a callable object that takes a single element from your vector (a std::string) and returns a bool. Right now, you're calling remove_if_found("violin") directly, which returns a bool (not a callable that can take a string). That's the immediate error.
Second, even if you fix the predicate, remove_if doesn't actually delete elements from the vector — it only moves elements you want to keep to the front, and returns an iterator pointing to the start of the "discarded" elements. You need to call erase after remove_if to shrink the vector to the correct size.
Solution 1: Use a Lambda Expression (C++11+)
This is the cleanest and most modern approach. Lambdas let you define the predicate inline, and you can capture the target word directly:
#include <iostream> #include <vector> #include <string> #include <algorithm> using namespace std; int main() { vector<string> data { "the guitar has six strings", "the violin has four strings", "the the violin is more difficult to learn", "saxophones are a family of instruments", "the drum is a set of percussions", "the trumpet is a brass" }; cout << data.size() << endl; // Output: 6 const string target_word = "violin"; // Use a lambda to check if each string contains the target word auto new_end = remove_if( data.begin(), data.end(), // Capture target_word by reference, check for substring existence [&target_word](const string& s) { return s.find(target_word) != string::npos; } ); // Erase the elements that were moved to the end data.erase(new_end, data.end()); cout << data.size() << endl; // Output: 4 return 0; }
Key Details:
[&target_word]: Captures thetarget_wordvariable by reference so the lambda can use it.s.find(target_word) != string::npos:std::string::findreturns the index of the first occurrence of the substring, orstring::nposif it's not found. This condition checks if the string contains the target word.data.erase(new_end, data.end()): Actually removes the unwanted elements from the vector, updating its size.
Solution 2: Use a Function Object (Functor)
If you're working with pre-C++11 code, or prefer a reusable predicate, you can define a struct with an overloaded operator():
#include <iostream> #include <vector> #include <string> #include <algorithm> using namespace std; struct ContainsWord { string target; // Constructor to store the target word ContainsWord(const string& t) : target(t) {} // Overload () to act as a unary predicate bool operator()(const string& s) const { return s.find(target) != string::npos; } }; int main() { vector<string> data { "the guitar has six strings", "the violin has four strings", "the the violin is more difficult to learn", "saxophones are a family of instruments", "the drum is a set of percussions", "the trumpet is a brass" }; cout << data.size() << endl; // Output: 6 auto new_end = remove_if(data.begin(), data.end(), ContainsWord("violin")); data.erase(new_end, data.end()); cout << data.size() << endl; // Output: 4 return 0; }
Solution 3: Use std::bind (C++11+)
You can also create a binary function (taking both the string to check and the target word) and bind the target word to make it a unary predicate:
#include <iostream> #include <vector> #include <string> #include <algorithm> #include <functional> // Needed for std::bind and placeholders using namespace std; using namespace std::placeholders; // For _1 placeholder // Binary function: checks if s contains word bool contains_word(const string& s, const string& word) { return s.find(word) != string::npos; } int main() { vector<string> data { "the guitar has six strings", "the violin has four strings", "the the violin is more difficult to learn", "saxophones are a family of instruments", "the drum is a set of percussions", "the trumpet is a brass" }; cout << data.size() << endl; // Output: 6 // Bind the second argument to "violin", leaving the first argument (_1) to be filled by remove_if auto new_end = remove_if(data.begin(), data.end(), bind(contains_word, _1, "violin")); data.erase(new_end, data.end()); cout << data.size() << endl; // Output: 4 return 0; }
Final Notes
- Always pair
remove_ifwitherase: This is called the "erase-remove idiom" — it's the standard way to remove elements from a container using STL algorithms. std::string::findis case-sensitive. If you need case-insensitive matching, you'd have to implement a custom check (e.g., converting both strings to lowercase before comparing).
内容的提问来源于stack exchange,提问作者Sergio

