Problem with find_if on map
I'm attempting to use std开发者_高级运维::find_if on a std::map, looking for a particular object that matches a string as follows:
class MyString
{
public:
MyString() {}
MyString(const std::string& x) : m_x(x) {}
const std::string& value() const
{
return m_x;
}
private:
std::string m_x;
};
std::map<int,MyString> squaresS;
std::map<int,MyString>::iterator iIt;
squaresS[1] = MyString("1");
squaresS[2] = MyString("4");
squaresS[3] = MyString("9");
const std::string sTarget = "4";
iIt = std::find_if(squaresS.begin(), squaresS.end(),
boost::bind(std::equal_to<std::string>(),
boost::bind(&MyString::value,
boost::bind(&std::map<int,MyString>::value_type::second, _1)),
sTarget));
if (iIt != squaresS.end())
std::cout << "Found " << iIt->second.value() << std::endl;
else
std::cout << "Not Found" << std::endl;
The result of executing this code is Not Found; I expected Found 4 to be output. However, if I do roughly the same thing using integers then it works i.e. the output is Found 4:
class MyInteger
{
public:
MyInteger() {}
MyInteger(int x) : m_x(x) {}
int value() const
{
return m_x;
}
private:
int m_x;
};
std::map<int,MyInteger> squaresI;
std::map<int,MyInteger>::iterator sIt;
squaresI[1] = MyInteger(1);
squaresI[2] = MyInteger(4);
squaresI[3] = MyInteger(9);
int iTarget = 4;
sIt = std::find_if(squaresI.begin(), squaresI.end(),
boost::bind(std::equal_to<int>(),
boost::bind(&MyInteger::value,
boost::bind(&std::map<int,MyInteger>::value_type::second, _1)),
iTarget));
if (sIt != squaresI.end())
std::cout << "Found " << sIt->second.value() << std::endl;
else
std::cout << "Not Found" << std::endl;
I suspect it is something to do with std::equal_to but I'm unsure how to go about fixing this.
Here is what you could do:
class MyString
{
public:
MyString() {}
MyString(const std::string& x) : m_x(x) {}
const std::string& value() const
{
return m_x;
}
private:
std::string m_x;
};
class mystringmatch
{
MyString _target;
public:
mystringmatch(const MyString& target):_target(target)
{
}
bool operator()(const std::pair<int, MyString>& src) const
{
return src.second.value() == _target.value();
}
};
int _tmain(int argc, _TCHAR* argv[])
{
std::map<int,MyString> squaresS;
std::map<int,MyString>::iterator iIt;
squaresS[1] = MyString("1");
squaresS[2] = MyString("4");
squaresS[3] = MyString("9");
const std::string sTarget = "4";
iIt = std::find_if(squaresS.begin(), squaresS.end(), mystringmatch(sTarget));
if (iIt != squaresS.end())
std::cout << "Found " << iIt->second.value() << std::endl;
else
std::cout << "Not Found" << std::endl;
return 0;
}
In my opinion, this kind of code makes people shift from C++ to other languages. It's nearly impossible to read.
sIt = std::find_if(squaresI.begin(), squaresI.end(),
boost::bind(std::equal_to<int>(),
boost::bind(&MyInteger::value,
boost::bind(&std::map<int,MyInteger>::value_type::second, _1)),
iTarget));
As noted by others, your code already works for me (VC++ 2010 SP1). That said, there is a trivial change that can be made to reduce the number of nested bind
s -- the return types of boost::bind
(unlike those of std::bind
) have all relational and logical operators overloaded including operator==
, obviating (or at least mitigating) the need for adapters like std::equal_to<>
. Taking advantage of this simplifies your code to something like:
typedef std::map<int, MyString> squares_t;
squares_t::const_iterator iIt = std::find_if(
squaresS.begin(),
squaresS.end(),
boost::bind(
&MyString::value,
boost::bind(&squares_t::value_type::second, ::_1)
) == sTarget
);
See the Boost.Bind docs for more info.
精彩评论