1

Function of interest

I am trying to write a templated sortDifferentWays function that will sort (and print) a vector following instructions of a comparator function. I am not sure what is the standard ways to indicate comparators but I went with

template<typename T>
void sortDifferentWays(std::vector<T>& v, std::vector<bool (*)(T,T)> comparators)
{
    for (auto& comparator : comparators)
    {
        std::sort(v.begin(), v.end(), comparator);
        printVec(v);
    }
}

Example of implementation

The function can for example be implemented in the code

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

class X
{
public:
    int a;
    int b;

    X (int A, int B)
    : a(A), b(B)
    {}

    std::string toString()
    {
        return "{"+std::to_string(a)+","+std::to_string(b)+"}";
    }
};

void printVec(std::vector<int> v)
{
    for (auto& elem : v)
        std::cout << elem <<" ";
    std::cout << "\n";
}

void printVec(std::vector<X> v)
{
    for (auto& elem : v)
        std::cout << elem.toString() <<" ";
    std::cout << "\n";
}

bool compare_a(X left, X right)
{
    return left.a < right.a;
}

bool compare_b(X left, X right)
{
    return left.b < right.b;
}

template<typename T>
void sortDifferentWays(std::vector<T>& v, std::vector<bool (*)(T,T)> comparators)
{
    for (auto& comparator : comparators)
    {
        std::sort(v.begin(), v.end(), comparator);
        printVec(v);
    }
}

int main()
{
    std::vector<X> v = {{1,5},{4,4},{9,2},{4,4},{0,6},{12,2},{11,9}};
    sortDifferentWays(v, {&compare_a, &compare_b});

    return 0;
}

, which outputs

{0,6} {1,5} {4,4} {4,4} {9,2} {11,9} {12,2} 
{9,2} {12,2} {4,4} {4,4} {1,5} {0,6} {11,9}

as expected. I welcome advice as to how improve this function but my main question is the following.

Question

I would now like sortDifferentWays to have a default comparators argument containing a single function which will work well with most (maybe all) primitive types.

template<typename T>
void sortDifferentWays(std::vector<T>& v, std::vector<bool (*)(T,T)> comparators = {[](const T& left, const T& right) -> bool{return left < right;}})
{
    for (auto& comparator : comparators)
    {
        std::sort(v.begin(), v.end(), comparator);
        printVec(v);
    }
}

int main()
{
    std::vector<X> v = {{1,5},{4,4},{9,2},{4,4},{0,6},{12,2},{11,9}};
    sortDifferentWays(v, {&compare_a, &compare_b});

    std::vector<int> v2 = {4,5,8,3,9,10,2};
    sortDifferentWays(v2);

    return 0;
}

But it fails to compile with error message

test.cpp:47:70: error: no matching constructor for initialization of 'std::vector<bool (*)(int, int)>'
void sortDifferentWays(std::vector<T>& v, std::vector<bool (*)(T,T)> comparators = {[](const T& left, const T& right) -> bool{return left < right;}})

I tried defining a defaultComparator as

template<typename T>
bool defaultComparator(const T& left, const T& right)
{
    return left < right;
}

and give comparators = {&defaultComparator} but it did not work either and produced the error message

error: reference to overloaded function could not be resolved; did you mean to call it?

How can I make a default comparator?

EDIT

I am compiling with

g++ test.cpp -o a -std=c++11

where g++ is defaulted to clang (MAC OSX)

g++ --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/4.2.1
Apple LLVM version 9.1.0 (clang-902.0.39.1)
Target: x86_64-apple-darwin17.5.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
Remi.b
  • 17,389
  • 28
  • 87
  • 168
  • are you sure that `sortDifferentWays(v)` is with `v` instead of `V` – JVApen Jun 03 '18 at 18:39
  • Oops! That's a small typo. Fixed now. It explains many of the compilation error I failed to understand though. Thank you! – Remi.b Jun 03 '18 at 18:48
  • For the others, see my reply – JVApen Jun 03 '18 at 18:50
  • Why? Why not just use `std::sort` with a custom comparison function/lambda? That's like one or two lines of code, whereas your solution is a ton of code to read through for no obvious gain.. – Jesper Juhl Jun 03 '18 at 18:59
  • Which compiler are you using? I cannot reproduce with gcc or clang. – Holt Jun 03 '18 at 19:09
  • @JesperJuhl My difficulty is in passing the function as aurgment and in defining a default. – Remi.b Jun 03 '18 at 19:11
  • @Holt I use `g++` which defaults to `clang` (MAC OS). – Remi.b Jun 03 '18 at 19:11
  • @Remi.b What happens in you force the conversion to function pointer by adding a `+` before `[]`? – Holt Jun 03 '18 at 19:20
  • @Hold Still the same error message. – Remi.b Jun 03 '18 at 19:22
  • @Remi.b I have clang clang-902.0.39.2 on MacOS and the code compiles fine (even without the `+`). Are you sure that this code triggers the error? – Holt Jun 04 '18 at 07:17
  • @Holt Yes I am sure. I have tried with another machine that has clang `clang-700.1.81` and it does not work either. I have a hard time to update to version `902.0.39.2` though. That's weird. Are you sure it works for you after overwriting the functions `sortDifferentWays` and `main` from **Example of implementation** with the ones from **Question**? – Remi.b Jun 04 '18 at 15:50
  • @Remi.b This is the exact code I tested: https://godbolt.org/g/GXME93 – Holt Jun 04 '18 at 15:54
  • Ah indeed that works :) But it breaks if we add `std::vector v2 = {4,5,8,3,9,10,2};sortDifferentWays(v2);` in `main`. – Remi.b Jun 04 '18 at 16:48

1 Answers1

1

I see a couple of problems in this example, however, the one you are searching for has to do with argument comparators.

This has been defined as std::vector<bool (*)(T,T)>, which is a vector of function pointers. However, you try to initalize it with {[](const T& left, const T& right) -> bool{return left < right;}}. This is an initializer_list of lambdas, which is not convertible.

The reason behind this, is that lambdas are a fast way of writing a class with operator() overloaded.

class lambda
{
public:
    template<typename T1, typename T2>
    auto operator()(const T1 &left, const T2 &right) const -> bool { return left < right; }
};

Obviously, this class ain't convertible to a function pointer. In order to handle both function pointers and classes (aka functors), you better use the type std::function from <functional>. In your case std::function<bool(T,T)>.

Edit Monkey0506 indicated that lambdas can be converted to function pointers in case there is no capture and they have the same signature. This last one isn't the case as the arguments of the lambda are const references.

JVApen
  • 11,008
  • 5
  • 31
  • 67
  • I am not used to using `std::function`. It'd be great if you could suggest a way to implement it for `sortDifferentWays`? – Remi.b Jun 03 '18 at 19:34
  • 1
    Changing `std::vector` to `std::vector>` should be sufficient from what I tested – JVApen Jun 03 '18 at 19:39
  • 1
    A non-capturing lambda [*should* be convertible to a function pointer](https://stackoverflow.com/questions/28746744/passing-lambda-as-function-pointer). – monkey0506 Jun 03 '18 at 20:51
  • I did not knew that and would not recommend it. Although in this case the argument ain't the same. – JVApen Jun 03 '18 at 20:55
  • 1
    @JVApen Lots of people would recommend against `std::function` due to their overload, which may have some impact here since it's used as a comparator function in `std::sort`. – Holt Jun 04 '18 at 07:17
  • @JVApen Your edit is wrong (or at least misleading) - This lambda is capture-less, the capture list is empty (`[]`). The fact that arguments of the lambda are taken as (const)-references does not impact this. – Holt Jun 04 '18 at 15:56
  • Tnx, I've fixed it – JVApen Jun 04 '18 at 18:11