c++overload-resolutionfunction-templatescompiler-bug

Is a function template accepting const char(&)[N] more specialized than function template accepting const T&?


I've defined two versions of a function template called compare:

#include <cstring>

using namespace std;

// fist version
template <size_t N, size_t M>
int compare(const char (&a)[N], const char (&b)[M]) {
    return strcmp(a, b);
}

// second version
template <typename T>
int compare(const T &a, const T &b) {
    if (a < b) return -1;
    if (b < a) return 1;
    return 0;
}

int main() {
    const char *p1 = "dog", *p2 = "cat";
    compare(p1, p2); // call second version
    compare("dog", "cat"); // call second version?

    return 0;
}

In the book CPP Primer (5th edition), which uses c++11std, the authors say compare(p1, p2) will call the second version template, because there is no way to convert a pointer to a reference to an array. The compare("dog", "cat") will call the second version of compare because it is more specialized than the first version.

However, when I run this code, I get a compiler error:

call of overloaded ‘compare(const char [4], const char [4])’ is ambiguous

Then, I changed compare("dog", "cat") to compare("dog", "cats"), and it can call the second version template with no problem.

Why does a and b having the same length cause the ambiguity?


Solution

  • tldr; This is CWG 2160 which points out that the current wording does not states/specify if conflicting deduced types during partial ordering should be invalid or ignored(valid). MSVC seems to ignore the conflicts due to the call compare("dog", "cat"); and accept the program while GCC and Clang seem to reject the conflicting deduced types during partial ordering and reject the program.


    Case 1: for the call compare(p1, p2);

    Version 1 is specifically written for const char arrays. For all other types, the second version is the viable option. Thus for the first call compare(p1, p2) the second version compare(const T &a, const T &b) should be selected as the first version compare(const char (&a)[N], const char (&b)[M]) is not even viable.


    Case 2: for the call compare("dog", "cat");

    Now coming to the second call compare("dog", "cat"). Here during partial ordering, when matching the transformed template of version 1 against original template version 2 conflicting types are deduced for T. Now the current wording doesn't specify if this conflicting behavior should be ignored or accepted. MSVC seems to ignore the conflicting deduced types while GCC and Clang seem to reject it. This is still an open issue. If conflicting types are ignored then this matching succeeds while if conflicting types are not to be ignored then this matching fails. So there are two possibilities in this first case.

    Doing the same process again, i.e., trying to match the transformed type of second template with the original template version 1 fails.


    Essentially, there are two possibilities depending on whether conflicting types are to be ignored or not during partial ordering:


    GCC rejects call to more specialized const char array version with string literal.