调用了错误的重载模板函数

Wrong overloaded template function is called

我有以下带有重载模板函数的代码

#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;
}

我原以为它会失败,因为具有三个参数的最大模板会return 引用一个临时变量(return 由指针模板编辑)。但它可以工作并调用通用模板函数。
问题是为什么不为指针调用模板?

谢谢。

没用。它似乎有效,因为您只使用了前两个参数或三个参数的 max 函数。

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
}

更正显示正在发生的事情:您正在比较指针地址。

参见 here in action

您的指针重载未被调用,因为引用版本更适合:a1 等是 const 引用(指向指针,但很好)。所以参考版本在重载解析方面是完美匹配的。

我想要实现你想要的,你需要一些 SFINAE 魔法。


我想补充一件事:由于比较指针是 C++ 中的常见操作,恕我直言,在比较之前对指针进行一些 max 取消引用会产生误导。

当你调用max(a, b, c)时,max(const T& a1, const T& a2, const T& a3)中的T成为int *的别名,所以在max(const T& a1, const T& a2, const T& a3 ) max(a,b) 将匹配 max(const T& a1, const T& a2)


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

如果您注释掉 2-argument max 的定义以供参考,您会发现代码无法编译。 MSVC++2013第22行给出的错误是:

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

这似乎就是为什么总是选择 max 引用的原因:max 指针的模板替换失败。 如果将代码更改为以下内容,则会调用指针模板:

#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;
}

如果注释掉指针模板的定义,则调用引用模板。指针模板优先于引用模板似乎是因为类型已经是指针。

这里对模板匹配顺序做一些解释:What are the rules for choosing from overloaded template functions?

编辑:可以用另一种方式更改 OP 的代码,以便 MSVC++2013 更喜欢基于引用的模板而不是基于指针的模板:

#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;
}

发生这种情况是因为在此版本中,基于指针的模板定义为其参数类型添加了额外的限定符:它们不仅仅是 T*,而是 const T*.