地图上的 find_if 问题

Posted

技术标签:

【中文标题】地图上的 find_if 问题【英文标题】:Problem with find_if on map 【发布时间】:2011-05-06 07:59:00 【问题描述】:

我正在尝试在 std::map 上使用 std::find_if,寻找与字符串匹配的特定对象,如下所示:

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;

执行这段代码的结果是Not Found;我希望 Found 4 被输出。但是,如果我使用整数做大致相同的事情,那么它可以工作,即输出是 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;

我怀疑这与 std::equal_to 有关,但我不确定如何解决此问题。

【问题讨论】:

无关评论:有时我只是惊讶于人们会被这种可怕的难以理解的语法一步一步地陶醉的程度。可能是关于boost的沸腾青蛙故事。 @6502,我听到了...一个简单的函子会比这种绑定魔法更容易理解...&lt;sigh/&gt; 实际上你上面的MyString 代码对我有用:ideone.com/DZ0lx,你仍然应该添加一个适当的复制构造函数! @Nim:同意。 C++0x 几乎就在这里,几乎没有什么理由可以避免新代码中的新特性。当然,我有点害怕人们会用algorithm 和 lambdas 编写的那种代码,而 for 就足够了:想想 jQuery 对 javascript 开发所做的事情,你就会明白我的意思了。跨度> 您的 MyString 代码也适用于 gcc-4.2.1 【参考方案1】:

你可以这样做:

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;

在我看来,这种代码使人们从 C++ 转向其他语言。几乎无法阅读。

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));

【讨论】:

【参考方案2】:

正如其他人所指出的,您的代码已经适用于我(VC++ 2010 SP1)。也就是说,可以进行一个微不足道的更改来减少嵌套 binds 的数量——boost::bind 的返回类型(与 std::bind 的返回类型不同)具有所有关系和逻辑运算符重载,包括 operator== ,消除(或至少减轻)对像std::equal_to&lt;&gt; 这样的适配器的需求。利用这一点可以将您的代码简化为:

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
);

请参阅Boost.Bind docs 了解更多信息。

【讨论】:

谢谢,我使用 VC++ 2005 编译这个,所以我想是时候升级了!我喜欢你对 operator()== 的使用。

以上是关于地图上的 find_if 问题的主要内容,如果未能解决你的问题,请参考以下文章

从地图容器中查找大于用户指定值的第一个值

在 find_if 中使用谓词

为啥 std::find_if(first, last, p) 不采用引用谓词?

find_if 中具有多个参数的 Lambda

在实例方法中使用 find_if

find_if 的两个条件