关于c ++:std :: find对象的成员

std::find Object by Member

情境

在使用STL的情况下,我遇到了一个超速跳车,这看起来很正常,此处简化了:

1
2
3
4
5
6
7
8
9
10
class Person {
  string Name;
  int    Age;
};

vector<Person> people;
AddPeople(people);

string s("Bob");
find(people.begin(), people.end(), s);


问题
不幸的是,find想要比较整个类。


问题
是否有更好或更合适的方式来执行" STL方法"?建议的问题没有帮助,但我设法找到了两个相关问题,但没有直接解决方案。


解决方法/测试

有一些可能的解决方法:

  • 完全放弃find(混乱,但可以重构):

    1
    2
    3
    4
    5
    6
    bool bBob = false;
    for (UINT i = 0; i < people.size(); i++) {
      if (people[i].Name == s)
      bBob = true;
      break;
    }
  • 提供转换运算符(隐式转换不起作用;显式不能在find中使用):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    class Person {
      string Name;
      int    Age;
      operator string() {return Name;}
    };

    Person b ("Bob", 99);
    string s ("Bob");
           b  == s;     //doesn’t work
    string(b) == s;     //works, but no good for find()
  • 定义一个独立的相等运算符(简单,有效,但全局公开):

    1
    2
    3
    BOOL operator==(Person l, string r) {
      return l.Name == r;
    }

  • 定义一个成员相等运算符(使比较顺序依赖;对象必须是第一个):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    class Person {
      string Name;
      int    Age;
      bool operator==(string s) {return Name == s;}
    };

    Person b ("Bob", 99);
    string s ("Bob");
    b==s;               //works
    s==b;               //doesn’t work, but not a problem for find()
  • 看起来#4是最佳人选,但似乎没有一个是理想的或没有" STL"的感觉,并且有些还存在问题。


    Is there a better or more appropriate way to do this the"STL way"?

    您可以使用std::find_if(由C ++ 11 lambdas驱动):

    1
    2
    3
    4
    std::string name ="Bob";
    // ...
    std::find_if(std::begin(people), std::end(people),
        [&] (Person const& p) { return p.Name == name; }

    注意,将其称为" STL方式"是不合适的。这是C ++标准库,而不是STL("标准模板库")。 STL是C ++标准库的容器和算法库的强烈启发,但是两者并不相同。有关更多信息,请参见StackOverflow上的此问答。

    编辑:

    由于使用的编译器不支持lambda,因此可以定义自己的函子谓词:

    1
    2
    3
    4
    5
    6
    7
    struct person_has_name
    {
        person_has_name(std::string const& n) : name(n) { }  
        bool operator () (Person const& p) { return p.Name == name; }
    private:
        std::string name;
    };

    并通过std::find_if将此方式使用:

    1
    2
    3
    std::string name ="Bob";
    // ...
    std::find_if(people.begin(), people.end(), person_has_name(name));


    我想您希望这些用于共享名为name的属性的子类或类,如果没有,您可以自己创建一个简单的函数。如果您希望它适用于具有属性名称的任何类,则可以创建如下模板:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    template < class ClassWithNameProperty >
    ClassWithNameProperty * searchName (std::string name,
                                        std::vector<ClassWithNameProperty *> array)
    {
        for (ClassWithNameProperty * obj: array)
        {
            if (obj.compare(obj->name))
            {
                return obj;
            }
        }
        return NULL;
    }

    或者,但是您希望搜索功能正常工作,希望这对您有所帮助


    有两种方法可以实现,所有方法都涉及某种可调用对象和std::find_if

    首先是使用新的C ++ 11 lambda:

    1
    2
    std::find_if(people.begin(), people.end(), [](const Person& person)
        { return person.Name =="Bob"; });

    如果您有不支持lambda的较旧的编译器,则可以使用functor对象:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class FindPersonByName
    {
        std::string name;

    public:
        FindPersonByName(const std::string& name) : name(name) {}

        bool operator()(const Person& person) const
            { return person.Name == name; }
    };

    std::find_if(people.begin(), people.end(), FindPersonByName("Bob"));

    当然,这两个都要求您的班级公开Name成员。但是您可以更改它以使用公共GetName函数代替,并将其添加到类中。