2

I have a class which offers custom static comparators which can be used by std::sort. The following would compile just fine (stripped down to a minimal code example):

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

class StringUtils
{
public:
    static bool customStringCompare(const std::string&, const std::string&) { return true; }
};

void test()
{
    std::vector<std::string> testList;
    std::sort(testList.begin(), testList.end(), StringUtils::customStringCompare);
}

Now, when I add an overload to the StringUtils class like

static bool customStringCompare(const char*, const char*) { return true; }

the following would work:

void test2()
{
    std::string s1, s2;
    StringUtils::customStringCompare(s1, s2);
}

However, the std::sort call above produces compiler error C2672 (No matching overload found), C2780 (expected 2 arguments - 3 supported), C2783 (template argument for "_Pr" could not be deduced) in MSVC 2015 Update 2.

Why does std::sort fail to find the matching overload in this case?

Tim Meyer
  • 12,210
  • 8
  • 64
  • 97
  • 2
    You should've post the complete error message, it says that it couldn't deduce template argument (because pointer to overloaded function is ambiguous). – user7860670 Sep 24 '18 at 08:42
  • @VTT I update the question to contain all three error codes . I did not post the full messages since they are german and the machine with the compiler is not connected to the internet unfortunately. – Tim Meyer Sep 24 '18 at 09:20

2 Answers2

6

In your code std::sort takes a function pointer. How then a compiler can decide which function you want? IntelliSense shows the following error:

cannot determine which instance of overloaded function StringUtils::customStringCompare is intended

To use overloading, you can turn a comparator into a function object:

struct Comparator {
    bool operator()(const std::string&, const std::string&) const {
        return true;
    }

    bool operator()(const char*, const char*) const {
        return true;
    }
};

void test() {
    std::vector<std::string> testList;
    std::sort(testList.begin(), testList.end(), Comparator{});
}

Alternatively, since C++14 you can use a generic lambda function:

void test() {
    std::vector<std::string> testList;
    std::sort(testList.begin(), testList.end(), 
        [](const auto& s1, const auto& s2) {
            return StringUtils::customStringCompare(s1, s2);
        });
}
Evg
  • 25,259
  • 5
  • 41
  • 83
  • I like the function object solution more than any of the solutions provided in the linked question: If I provide such a struct in addition to the comparator functions, users of the class don't have to care about type deduction. This simplifies both caller code and unit tests and should thus provide both higher maintainability and robustness. – Tim Meyer Sep 25 '18 at 07:44
2

The issue is that there are two overloads and passing one to std::sort does not clarify which overload should be used1. The compiler cannot deduce this from the usage in the std::sort call. This makes sense: The type of the comparator argument of std::sort is simply a template argument, i.e. it’s completely unspecified: any overload works just as well as any other.

There are multiple workarounds and in practice I generally recommend passing a functor as shown in Evg’s answer.

But it’s important to understand that the error is simply caused by a type that cannot be deduced automatically. So, to make the code compile, it’s sufficient to specify the type explicitly; this will select a single overload:

std::sort(
    testList.begin(),
    testList.end(),
    static_cast<bool (*)(std::string const&, std::string const&)>(StringUtils::customStringCompare)
);

Here we use static_cast to explicitly signal the type of the function (pointer) for the purpose of overload resolution.


1 And, frankly, every single mainstream compiler produces a rotten error message. This has been known for a long time and is entirely fixable. clang++ is slightly better than GCC and MSVC but honestly not by much. But even C#, a completely unrelated language, gives highly idiosyncratic errors in similar situations.

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • Using the above static cast I get: cannot convert from 'bool (__cdecl *)(const string &,const string &)' to 'bool (__cdecl *)(const string &,const string &)' Visiul Studio 2017 Community –  Sep 03 '19 at 12:31