2

I am using a set of pointers to a class, and have provided a compare function to order the set by comparing the objects rather than the pointers. I am now trying to write another function and to provide it with the same compare function as used in the set, as follows:

/* compset.cpp  -  a program to test compare function of std::set  */
#include <cstdio>
#include <cstdlib>
#include <set>

class foo {
    int n;

public:
    foo(int nn)
        : n(nn)
    {
    }
    friend bool operator<(const foo& a, const foo& b);
};

bool operator<(const foo& a, const foo& b)
{
    return (a.n < b.n);
}

struct fooislt {
    bool operator()(foo* a, foo* b) { return (*a < *b); }
};

class bar {
    int m;

public:
    bar(int mm)
        : m(mm)
    {
    }
    void func(std::set<foo*, fooislt>::iterator ita,
        std::set<foo*, fooislt>::iterator itb, bool (*comp)(foo*, foo*));
};

void bar::func(std::set<foo*, fooislt>::iterator ita,
    std::set<foo*, fooislt>::iterator itb, bool (*comp)(foo*, foo*))
{
    if (comp(*ita, *itb)) {
        // Do something
    }
}

int main(int argc, char* argv[])
{
    bar bar1(17);
    std::set<foo*, fooislt> myset;
    // set up myset
    std::set<foo *, fooislt>::iterator it1, it2;
    // determine it1 and it2
    bar1.func(it1, it2, myset.key_comp);
}

The error message from g++ is:

compset.cpp: In function ‘int main(int, char**)’: compset.cpp:40:27: error: invalid use of non-static member function ‘std::set<_Key, >_Compare, _Alloc>::key_compare std::set<_Key, _Compare, _Alloc>::key_comp() const [with >_Key = foo*; _Compare = fooislt; _Alloc = std::allocator<foo*>; std::set<_Key, _Compare, >_Alloc>::key_compare = fooislt]’ 40 | bar1.func(it1,it2,myset.key_comp); | ~~~~~~^~~~~~~~ In file included from /usr/include/c++/10/set:61, from compset.cpp:5: /usr/include/c++/10/bits/stl_set.h:327:7: note: declared here 327 | key_comp() const | ^~~~~~~~

How can I make this work?

Marek R
  • 32,568
  • 6
  • 55
  • 140
ghborrmann
  • 101
  • 6
  • 1
    `key_comp` is a member function whose return value is the actual binary comparison used. I think you want `bar.func(it1, it2, myset.key_comp())`. EDIT: No, that's still not quite a function pointer. Hopefully this gets someone else on the right track though. – Nathan Pierson Mar 08 '21 at 15:31
  • Another possibility instead of templates is to take an argument of type `std::function` instead of a function pointer. – Nathan Pierson Mar 08 '21 at 15:47
  • 1
    Use `std::set::key_compare comp` for declaring comp function. Return type of key_comp() is a instace of key_compare class and not a function pointer. https://www.cplusplus.com/reference/set/set/key_comp/ – lastbreath Mar 08 '21 at 15:48

2 Answers2

2

What is the problem.

Problem is that compactor function is something more complex then primitive pointer for function.

With definition like this: std::set<foo*, fooislt> compactor is a class of type fooislt which has defined operator(). Now since this is an object it can't be converted to pointer to function.

Simple solution is to use template of bar::func so compactor function will adopt to proper type: https://godbolt.org/z/3Whn89

class bar {
    int m;

public:
    bar(int mm)
        : m(mm)
    {
    }

    template <typename F>
    void func(std::set<foo*, fooislt>::iterator ita,
        std::set<foo*, fooislt>::iterator itb, F comp);
};

template <typename F>
void bar::func(std::set<foo*, fooislt>::iterator ita,
    std::set<foo*, fooislt>::iterator itb, F comp)
{
    if (comp(*ita, *itb)) {
        // Do something
    }
}

Alternatively you can use std::function<> which hides template use is able to perform proper conversion. https://godbolt.org/z/frcjdK

class bar {
    int m;

public:
    bar(int mm)
        : m(mm)
    {
    }

    void func(std::set<foo*, fooislt>::iterator ita,
        std::set<foo*, fooislt>::iterator itb, std::function<bool(foo*, foo*)> comp);
};

void bar::func(std::set<foo*, fooislt>::iterator ita,
    std::set<foo*, fooislt>::iterator itb, std::function<bool(foo*, foo*)> comp)
{
    if (comp(*ita, *itb)) {
        // Do something
    }
}
Marek R
  • 32,568
  • 6
  • 55
  • 140
0

I've found, thanks to the comment of ani1998ket and the suggestion of a template by Marek R, what I think is the best solution to my problem: using a template to define the comparison function, and using std::set::key_compare as the type in the definition of bar::func as follows:

/* compset.cpp  -  a program to test compare function of std::set  */

#include <stdio.h>
#include <stdlib.h>
#include <set>
template<typename T>
struct isltptr {
  bool operator()(T *t1,T *t2) { return(*t1<*t2); }
};
class foo {
  int n;
public:
  foo(int nn) : n(nn) {}
  bool operator<(const foo &a) { return(n<a.n); }
};
class bar {
  int m;
public:
  bar(int mm) : m(mm) {}
  void func(std::set<foo *,isltptr<foo> >::iterator ita,
        std::set<foo *,isltptr<foo> >::iterator itb,
        std::set<foo *,isltptr<foo> >::key_compare comp);
};
void bar::func(std::set<foo *,isltptr<foo> >::iterator ita,
        std::set<foo *,isltptr<foo> >::iterator itb,
        std::set<foo *,isltptr<foo> >::key_compare comp)
{
  if (comp(*ita,*itb)) {
    // Do something
  }
}
int main(int argc,char *argv[])
{
  bar bar1(17);
  std::set<foo *,isltptr<foo> > myset;
  // set up myset
  std::set<foo *,isltptr<foo> >::iterator it1,it2;
  // determine it1 and it2
  bar1.func(it1,it2,myset.key_comp());
}

I find the template useful since I have other sets of pointers needing the same functionality.

ghborrmann
  • 101
  • 6