35

When using the STL sort algorithm on a vector, I want to pass in my own comparison function which also takes a parameter.

For example, ideally I want to do a local function declaration like:

int main() {
    vector<int> v(100);
    // initialize v with some random values

    int paramA = 4;

    bool comp(int i, int j) {
        // logic uses paramA in some way...
    }

    sort(v.begin(), v.end(), comp);
}

However, the compiler complains about that. When I try something like:

int main() {
    vector<int> v(100);
    // initialize v with some random values

    int paramA = 4;

    struct Local {
        static bool Compare(int i, int j) {
            // logic uses paramA in some way...
        }
    };

    sort(v.begin(), v.end(), Local::Compare);
}

The compiler still complains: "error: use of parameter from containing function"

What should I do? Should I make some global variables with a global comparison function..?

Thanks.

Nietzche-jou
  • 14,415
  • 4
  • 34
  • 45
George41
  • 685
  • 2
  • 7
  • 6

4 Answers4

35

You cannot access the local variables of a function from within a locally defined function -- C++ in its current form does not allow closures. The next version of the language, C++0x, will support this, but the language standard has not been finalized and there is little support for the current draft standard at the moment.

To make this work, you should change the third parameter of std::sort to be an object instance instead of a function. The third parameter of std::sort can be anything that is callable (i.e. any x where adding parentheses like x(y, z) makes syntactic sense). The best way to do this is to define a struct that implements the operator() function, and then pass an instance of that object:

struct Local {
    Local(int paramA) { this->paramA = paramA; }
    bool operator () (int i, int j) { ... }

    int paramA;
};

sort(v.begin(), v.end(), Local(paramA));

Note that we have to store paramA in the structure, since we can't access it otherwise from within operator().

Adam Rosenfield
  • 390,455
  • 97
  • 512
  • 589
  • 1
    The third parameter is anything that can be called using function call syntax. So both a function and a class/struct that defines `operator()` will do the trick. – Eugen Constantin Dinca Nov 01 '10 at 04:17
  • Thanks, that's working (except that the compiler complains unless I move the struct declaration outside the main function. I thought we were allowed to declare classes and structs locally..?) – George41 Nov 01 '10 at 04:39
  • This should work. Try posting a separate question about those complaints. – Basilevs Nov 01 '10 at 07:33
  • Can the same thing be done with `std::map`? If so, how would the syntax be for passing an instance of the struct to the map? – tjespe Sep 17 '19 at 09:07
16

In C++ you cannot define a free function inside another function. So your first code snippet is ill formed.

sort(v.begin(), v.end(), Local::Compare);

The 3rd argument must be a function object. Overload () operator inside the class and then create the function object.


In C++0x you can use lambda expressions.

auto comp = [&](int m,int n)-> bool {

        return m<n; //or use paramA in some way
    };

sort(v.begin(), v.end(), comp);
Prasoon Saurav
  • 91,295
  • 49
  • 239
  • 345
  • 2
    In my opinion a much better solution than the excepted one because of using nice c++ features (lambda) – Anonymous Mar 27 '15 at 12:50
6

One possibility is to pass the parameter when you construct your comparator object:

class cmp {
    int param;
public:
    cmp(int p) : param(p) {}

    bool operator()(int i, int j) {
        // logic uses param
    }
};

int main() {
    vector<int> v(100);
    // initialize v with some random values

    int paramA = 4;

    sort(v.begin(), v.end(), cmp(paramA));
}
Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
1

//Using std::bind

//Example

{
vector<int> vecInt{2, 4, 10, 20, 30};
    int i = 4;
    sort(vecInt.begin(), vecInt.end(), std::bind( [](int a, int b, int c)
    {
        return abs(a - c) < abs(b - c);
    }, std::placeholders::_1, std::placeholders::_2, i)
    );
}
dang son
  • 11
  • 1
  • 1
    This was asked before `std::bind` (and lambdas) was part of the standard. And it's much simpler to have a capturing lambda than `bind`ing a non-capturing lambda – Caleth Jan 11 '19 at 09:46