user1289
user1289

Reputation: 1321

Wrong overloaded template function is called

I have the following code with overloaded template functions

#include <iostream>
using namespace std;

template <class T>
const T& max(const T& a1, const T& a2)
{
    cout << "general template" << endl;
    return (a1 < a2) ? a2 : a1;
}

template <class T>
const T* max(const T* a1, const T* a2)
{
    cout << "max for pointers" << endl;
    return (a1 < a2) ? a2 : a1;
}

template <class T>
const T& max(const T& a1, const T& a2, const T& a3)
{
    cout << "general template with three parameters" << endl;
    return ::max(::max(a1, a2), ::max(a1, a2));
}

int main()
{
    int* a = new int(5);
    int* b = new int(56);
    int* c = new int(2);
    int*const  &g = ::max(a, b, c);
    cout << *g << endl;

    return 0;
}

I was expecting it to fail, because the max template with three parameters would return reference to a temporary variable (returned by template for pointers). But it works and calls general template function.
The question is why doesn't called template for pointers?

Thanks.

Upvotes: 2

Views: 934

Answers (3)

kiviak
kiviak

Reputation: 1103

When you call max(a, b, c),T in max(const T& a1, const T& a2, const T& a3) becomes an aliases of int *,so in max(const T& a1, const T& a2, const T& a3) max(a,b) will match max(const T& a1, const T& a2)


typedef int * T;
const T x;
const int * y;  //they are different

Upvotes: 1

Serge Rogatch
Serge Rogatch

Reputation: 15100

If you comment out the definition of 2-argument max for references, you would see that the code does not compile. The error that MSVC++2013 gives at line 22 is:

error C2440: 'return' : cannot convert from 'const int *' to 'int *const &'

That seems to be the reason why max for references is always selected: template substitution fails for max for pointers. If you change the code to the following, then template for pointers is called:

#include <iostream>
using namespace std;

template <class T>
T max(const T& a1, const T& a2)
{
    cout << "general template" << endl;
    return (a1 < a2) ? a2 : a1;
}

template <class T>
T* max(T* a1, T* a2)
{
    cout << "template for pointers" << endl;
    return (a1 < a2) ? a2 : a1;
}

template <class T>
T max(const T& a1, const T& a2, const T& a3)
{
    cout << "general template with three parameters" << endl;
    return ::max(::max(a1, a2), a3);
}

int main()
{
    int* a = new int(5);
    int* b = new int(56);
    int* c = new int(2);
    int* g = ::max(a, b, c);
    cout << *g << endl;

    return 0;
}

If you comment out the definition of the template for pointers, then the template for references is called. The preference of template for pointers over template for references seems to happen because the type is already a pointer.

Here is some explanation on the order of template matching: What are the rules for choosing from overloaded template functions?

EDIT: the OP's code can be altered in another way, so that MSVC++2013 prefers reference-based template to pointer-based:

#include <iostream>
using namespace std;

template <class T>
T max(const T& a1, const T& a2)
{
    cout << "general template" << endl;
    return (a1 < a2) ? a2 : a1;
}

template <class T>
const T* max(const T* a1, const T* a2)
{
    cout << "template for pointers" << endl;
    return (a1 < a2) ? a2 : a1;
}

template <class T>
T max(const T& a1, const T& a2, const T& a3)
{
    cout << "general template with three parameters" << endl;
    return const_cast<const T>(::max(::max(a1, a2), a3));
}

int main()
{
    int* a = new int(5);
    int* b = new int(56);
    int* c = new int(2);
    int* g = ::max(a, b, c);
    cout << *g << endl;

    return 0;
}

This happens because in this version pointer-based template definition has additional qualifiers for its parameter types: they are not just T*, but rather const T*.

Upvotes: 2

Daniel Jour
Daniel Jour

Reputation: 16156

It's not working. It seems to be working, because you're only using the first two parameters or your three parameter max function.

template <class T>
const T& max(const T& a1, const T& a2, const T& a3)
{
    cout << "general template with three parameters" << endl;
    return ::max(
         ::max(a1, a2),
         ::max(a1, a2)); // HERE
}

Correcting that shows what is going on: You're comparing pointer addresses.

See here in action.

Your pointer overload isn't called because the reference version is a better fit: a1 etc ARE const references (to pointers, but well). So the reference version is a perfect match on respect to overload resolution.

I guess to achieve what you want you'd need some SFINAE magic here.


One thing I'd like to add: since comparing pointers is a frequent operation in C++, IMHO it would be very misleading to have some max dereference the pointers before comparing.

Upvotes: 2

Related Questions