简体   繁体   中英

Any way to do a std::upper_bound on a member variable?

I want to use std::upper_bound to find the span of objects in some container which are less or equal to a provided value. This makes it a nice simple one-liner!

The problem is I'm interested only in comparing against a specific primitive member of the class. Sorting the container is no problem, but when I want to use std::upper_bound , I need to provide an object to compare against for the function to work.

For an MCVE, suppose I have a bunch of people and I want to find an iterator to:

struct Person {
    int age;
    double height;

    Person(int age, double height) : age(age), height(height) { }
};

int main() {
    vector<Person> people = { 
        Person(5, 12.3), 
        Person(42, 9.6), 
        Person(38, 18.4), 
        Person(31, 8.5)
    };

    auto sorter = [](const Person& a, const Person& b) {
        return a.height < b.height;
    };

    std::sort(people.begin(), people.end(), sorter);

    // All I care about is comparing against this number
    // Instead... I have to create a whole new struct
    //double cutoff = 10.0;
    Person cutoff(123, 10.0);
    auto it = std::upper_bound(people.begin(), people.end(), cutoff, sorter);

    // Do stuff with 'it' here
}

The problem I have is that I need to instantiate the whole object just to use std::upper_bound , like I do in the code above. I can't have a 'comparator against the value I'm providing'. This makes it very annoying because the objects I'm comparing against aren't easy for me to pop into existence without doing a fair amount of work.

Are there any viable strategies to get around this that will lead to the cleanest and most compact code I can find? For example, it'd have been nice if I could have done (for the MCVE):

auto cutoffCompare = [](const Person& p, const double height) { 
    return p.height < height;
};

// Doesn't exist (AFAIK?)
auto it = std::upper_bound(people.begin(), people.end(), cutoff, sorter, cutoffCompare);

Due to it being in aa hot spot in the program where I care about performance more than normal, I can't do something like transform the objects into the primitive type and then do upper_bound on that new list. I can create a whole new object and use it as a dummy, but then I'll be adding an annoying amount of code to do something very simple. Am I stuck instantiating the object? Or do I have to roll my own upper_bound?

There's no requirement that the value passed to std::upper_bound has to match the iterator's type, it could be anything you want if you provide the right comparison function. You were pretty close with your desired sample, just need to flip the arguments. The docs here indicate the comparison function takes in the limit value as the first argument.

auto cutoffCompare = [](double height, const Person& p) { 
    return p.height < height;
};

auto it = std::upper_bound(people.begin(), people.end(), 10.0, cutoffCompare);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM