Search code examples
c++templatestypename

Wrong template function deduction


Unexpected function deduction is happening in the following program. -

Case I -

template <typename T, typename>
void foo(T x, int) {
    cout << "In int foo\n";
}

template <typename T>
void foo(T x, char) {
    cout << "In char foo\n";
}

int main() {
    string s = "Hello World";
    foo(s, (int) 1);
}

Output

In char foo

Case II -

template <typename T>
void foo(T x, int) {
    cout << "In int foo\n";
}

template <typename T>
void foo(T x, char) {
    cout << "In char foo\n";
}

int main() {
    string s = "Hello World";
    foo(s, (int) 1);
}

Output

In int foo

For case II, I just removed the last template parameter typename from the first function. Could someone please explain what's happening here?


Solution

  • In this function,

    template <typename T, typename>
    void foo(T x, int) {
        cout << "In int foo\n";
    }
    

    You tell the compiler that,

    1. There are 2 template parameters.
    2. The return is void.
    3. Takes two parameters, one from type T and the other is int.
    4. Then the function body.

    When compiling foo(s, (int) 1);, the compiler knows the type of T as its given by the argument, the second argument is also given. But the second template argument is unknown. So the compiler tries to find the other most suitable one, which in this case the other foo function.

    This time the compiler tests it out once again and it passes, as int can be implicitly converted to char. This is why it outputs In char foo in the first case.

    In the second case, since there are no unknown types, the most suitable one is the first function void foo(T x, int). That's because the second argument type is int and the function's second parameter types is also int.

    To resolve the issue in the first case, you can provide a default type argument to the template. This way it knows the second type parameter, thus will work without an issue.

    template <typename T, typename = void>
    void foo(T x, int) {
        cout << "In int foo\n";
    }