Please help me about std::find, thanks a lot

This code about Nstring - it d “numbered string” that keeps track of the number of occurrences of the word it contains.

even through having:

bool operator==(const psi& l, const psi& r) {
  return l.first == r.first;
}

the std::find still compares entirety ‘psi object’, not compares psi::first

so I can only replace that:

for(vp::iterator beg = words.begin(); beg < words.end(); beg++){
            if(beg->first == x){
                thisOccurrence = ++beg->second;
                return;
            }

please tell me why?!
that’s entirtly code:(noticed NString::addString)

//: C06:NString.h
// From "Thinking in C++, Volume 2", by Bruce Eckel & Chuck Allison.
// (c) 1995-2004 MindView, Inc. All Rights Reserved.
// See source code use permissions stated in the file 'License.txt',
// distributed with the code package available at www.MindView.net.
// A "numbered string" that keeps track of the
// number of occurrences of the word it contains.
#ifndef NSTRING_H
#define NSTRING_H
#include <algorithm>
#include <iostream>
#include <string>
#include <utility>
#include <vector>
typedef std::pair<std::string, int> psi;

// Only compare on the first element

bool operator==(const psi& l, const psi& r) {
  return l.first == r.first;
}



class NString {
  std::string s;
  int thisOccurrence;
  // Keep track of the number of occurrences:
  typedef std::vector<psi> vp;
  typedef vp::iterator vpit;
  static vp words;
  void addString(const std::string& x) {
    psi p(x, 0);
    
    //that's a code I replaced below
    /*------------------------------------------------------------------------------------------
    for(vp::iterator beg = words.begin(); beg < words.end(); beg++){
            if(beg->first == x){
                thisOccurrence = ++beg->second;
                return;
            }
    }
    thisOccurrence = 0;
    words.push_back(p);
    */-------------------------------------------------------------------------------------------------------
    
    //that's original code!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    //*-------------------------------------------------------------------------------------------------------
    vpit it = std::find(words.begin(), words.end(), p); 

    if(it != words.end())
      thisOccurrence = ++it->second;
    else {
      thisOccurrence = 0;
      words.push_back(p);
    }
    //------------------------------------------------------------------------------------------------------
  }
public:
  NString() : thisOccurrence(0) {}
  NString(const std::string& x) : s(x) { addString(x); }
  NString(const char* x) : s(x) { addString(x); }
  // Implicit operator= and copy-constructor are OK here.
  friend std::ostream& operator<<(
    std::ostream& os, const NString& ns) {
    return os << ns.s << " [" << ns.thisOccurrence << "]";
  }
  // Need this for sorting. Notice it only
  // compares strings, not occurrences:
  friend bool
  operator<(const NString& l, const NString& r) {
    return l.s < r.s;
  }
  friend
  bool operator==(const NString& l, const NString& r) {
    return l.s == r.s;
  }
  // For sorting with greater<NString>:
  friend bool
  operator>(const NString& l, const NString& r) {
    return l.s > r.s;
  }
  // To get at the string directly:
  operator const std::string&() const { return s; }

};

// Because NString::vp is a template and we are using the
// inclusion model, it must be defined in this header file:
NString::vp NString::words;
#endif // NSTRING_H ///:~

thanks a lot

The answer is “because of how C++ resolves identifiers”.

The short explanation of what happens is:

  • The compiler tries to find operator==
  • It looks in the std namesapce and finds std::operator==(const std::pair&, const std::pair&)
  • std::operator==(const std::pair&, const std::pair&) is a suitable match
  • The compiler doesn’t look outside std because it already found a suitable match

There’s actually more to it than that, but the full process is really compilcated.
You can read about that here if you want:

You can work around this problem by using std::find_if instead.

If you’ve got access to C++11 then the easiest way is this:

// Infer the compiler-specific type of the lambda
const auto predicate = [](const psi & left, const psi & right)
{
	return (left.first == right.first);
};

// Infer the iterator type
auto iterator = std::find_if(std::begin(words), std::end(words), predicate);

if(iterator != std::end(words))
{
	++it->second;
	this->thisOccurrence = it->second;
}
else
{
	this->thisOccurrence = 0;
	words.push_back(p);
}

Normally I’d complain about some of the style issues in the code and suggest some better alternatives,
but I don’t really have time for that at the moment, so I’ll possibly edit them in later.

Thanks Pharap!!!
I can only use function object methed successful. like this:

  struct psiCompareEq : std::binary_function<psi, psi, bool>{
    bool operator()(psi a, psi b) const { return a.first == b.first; }
  };
  
  void addString(const std::string& x) {
    psi p(x, 0);

    vpit it = std::find_if(words.begin(), words.end(), std::bind2nd(psiCompareEq(), p));
    if(it != words.end())
      thisOccurrence = ++it->second;
    else {
      thisOccurrence = 0;
      words.push_back(p);
    }

but fail to directly using lambda expression and ptr_fun(). could you tell me how to using these ?
just like:

  const bool psicpeq(const psi a, const psi b) const { return a.first == b.first;}

  void addString(const std::string& x) {
    psi p(x, 0);

    vpit it = std::find_if(words.begin(), words.end(), std::bind2nd(std::ptr_fun<psi,psi,bool>(psicpeq)(), p));
    if(it != words.end())
      thisOccurrence = ++it->second;
    else {
      thisOccurrence = 0;
      words.push_back(p);
    }

  }

the complier shows “D:\c++\test\NString.h|42|error: no matching function for call to ‘ptr_fun()’|”

Thanks

That’s my mistake.

I thought the 3rd parameter was supposed to be a binary predicate (like operator ==),
but it’s supposed to be a unary predicate (which in hindsight makes more sense.

So my earlier example should be:

// Infer the compiler-specific type of the lambda
// Make sure to capture 'p'
const auto predicate = [&p](const psi & other)
{
	return (other.first == p.first);
};

// Infer the iterator type
auto iterator = std::find_if(std::begin(words), std::end(words), predicate);

if(iterator != std::end(words))
{
	++it->second;
	this->thisOccurrence = it->second;
}
else
{
	this->thisOccurrence = 0;
	words.push_back(p);
}

Both std::bind2nd and std::ptr_fun were deprecated in C++11 and removed in C++17, so you should avoid using them.

I think they were removed because lambdas are a much better alternative.
(I hadn’t even heard of them until now. I’ve certainly never seen them used in any other code.)

I think <psi, psi, bool> is actually incorrect here anyway because you defined psicpeq as const bool psicpeq(const psi a, const psi b).

psicpeq is a member function, not a static or free function,
so a normal function pointer can’t be used, which means ptr_fun can’t be used because it expects a normal function pointer, not a member function pointer:

template< class Arg1, class Arg2, class Result >
std::pointer_to_binary_function<Arg1,Arg2,Result>
    ptr_fun( Result (*f)(Arg1, Arg2) );

Thanks a lot!!!

1 Like