简体   繁体   中英

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.

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.

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* .

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM