1

I have a vector of objects:

struct Student{   
  string name;   
  string id;   
  string major;
  int age;
};

vector<Student> s;

Is there any way to write ONE general (maybe template) function to sort this vector (or array) based on different fields instead of writing four different functions?

user3112666
  • 1,689
  • 1
  • 12
  • 12
  • 1
    The standard library provides an overload for `std::sort` which accepts a binary predicate, you could define your own comparison using a lambda/functor. [See examples here](https://en.cppreference.com/w/cpp/algorithm/sort). – Mansoor Dec 01 '19 at 22:45
  • 1
    Yes, it's possible. Write a binary comparator predicate type that, based on its internal state, compares different fields. You have to write one at least somewhat anyway, as no one knows how to sort `Student` objects without one. Just make it flexible to allow specifying which field(s) to sort by. – WhozCraig Dec 01 '19 at 22:46

1 Answers1

5

I think the previous comments all said that it is possible to write such a comparison function. But if I understand you correctly, you want one function for all 4 comparisons (maybe in a templated way). There is indeed, when using member object pointers (EDIT: was member function pointers, thanks to @WhozCraig for pointing it out):

#include <vector>
#include <algorithm>
#include <iostream>

struct Student {
    std::string name;
    std::string id;
    std::string major;
    int age;
};

template<typename T>
struct Comparator {
    const T Student::* member;

    bool operator()(const Student& stu1, const Student &stu2) const
    {
        return stu1.*member < stu2.*member;
    }

};


int main()
{
    Comparator<int> cint{&Student::age};
    Comparator<std::string> cstring{&Student::name};

    std::vector<Student> vec = {{"Paul", "P", "Mathematics", 42}, {"John", "J", "Computer Science", 43}};

    std::sort(begin(vec), end(vec), cint);
    for(auto &s: vec)
    {
        std::cout << s.age << "\n";
    }

    std::sort(begin(vec), end(vec), cstring);
    for(auto &s: vec)
    {
        std::cout << s.name << "\n";
    }

    return 0;
}

Note that templating wouldn't even be necessary if all your member variables were of the same type. You could also provide an overload for Comparator<int> that default initializes member with &Student::age since there is only one int member, this would reduce the writing effort a bit.

But I think that concerning runtime speed, a lambda in place could be faster.

n314159
  • 4,990
  • 1
  • 5
  • 20
  • *"There is indeed, when using member function pointers:"* - for the record, those aren't member function pointers; those are member *variable* pointers. Outside of that, interesting idea. – WhozCraig Dec 02 '19 at 01:04