关于C++模板与非模板函数重载

本文最后更新于:2022年4月26日 下午

前情提要

假如我们需要为QList<T>的某个类型编写特殊的operator==

那就需要考虑是编写普通函数还是模板特化了

模板函数与非模板函数的重载决议步骤

1.为这个函数名建立候选函数集合

  • 与被调用函数名字相同的任意普通函数
  • 任意函数模板实例化,在其中,模板实参推断发现了与调用中所用函数实参相匹配的模板实参

2.确定哪些普通函数是可行的(如果有可行函数的话)。候选集合中的每个模板实例都可行的,因为模板实参推断保证函数可以被调用

3. 如果需要转换来进行调用,根据转换的种类排列可靠函数,记住,调用模板函数实例所允许的转换是有限的

  • 如果只有一个函数可选,就调用这个函数
  • 如果调用有二义性,从可行函数集合中去掉所有函数模板实例

4.重新排列去掉函数模板实例的可行函数

  • 如果只有一个函数可选,就调用这个函数
  • 否则,调用有二义性

问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
template <class T>
class MyList {
public:
bool operator==(const MyList<T>& l) const {
return a == l.a;
}
bool operator!=(const MyList<T>& l) const {
return a != l.a;
}
T a;
};

bool operator==(const MyList<int>& lhs, const MyList<int>& rhs)
{
return lhs.a == rhs.a;
}

bool operator!=(const MyList<int>& lhs, const MyList<int>& rhs)
{
return !(lhs == rhs);
}

int main(void){
MyList<int> list, list2;
list == list2;
list != list2;
}

如上图 调用!=运算符会报错

1
2
3
E:\Qt5.14.2\Projects\HelloWorld\hellodialog.cpp:44: error: ambiguous overload for 'operator==' (operand types are 'const MyList<int>' and 'const MyList<int>')
return !(lhs == rhs);
~~~~^~~~~~

也就是类模板的成员函数 和 普通函数产生二义性(都是完美匹配)

但是你可能说了:函数模板优先级不应该低于普通函数吗,应该调用普通函数才对呀

我也这么认为

但是有没有可能,成员函数operator==不是模板函数呢?

因为唯一的模板适用于指示模板类的类型的

一旦类的类型确定,operator==的参数也就确定了

在类模板作用域中,可以直接使用模板名而不提供实参 —— 《C++ Primer》 P589

即,以下两种皆可

1
2
bool operator==(const MyList<T>& l) const;
bool operator==(const MyList& l) const;

这就是普通函数

operator==根本不是模板函数

导致模板函数匹配规则不适用

(以上我猜的)

第二点

1
2
3
E:\Qt5.14.2\Projects\HelloWorld\hellodialog.cpp:64: warning: ISO C++ says that these are ambiguous, even though the worst conversion for the first is better than the worst conversion for the second:
list == list2;
^~~~~

这个是warning,如果按以上分析,此时虽然要经过const转换,这应该也得是二义性错误,为什么此时普通函数更好,不解

正确做法

想要不冲突,最好不要产生模板函数和普通函数的重载

最好进行模板特化

1
2
3
4
5
6
7
8
9
10
template <>
bool MyList<int>::operator==(const MyList<int>& l) const
{
return l.a == a;
}
template <>
bool MyList<int>::operator!=(const MyList<int>& l) const
{
return !(l == *this);
}

PS:注意模板特化要写在调用之前,否则会导致产生新模板实例

Ref

C++普通函数与模板函数以及特化函数重载的优先级问题 - Ricky.K - 博客园

聊聊C++模板函数与非模板函数的重载 - origins - 博客园

函数模板与同名的非模板函数重载的时候,两者调用顺序_九月丫头的博客-CSDN博客

类模板函数特化(专用化)specialization of …… after instantiation_布鲁克零三四四的博客

C++模板的偏特化与全特化 | Harttle Land


关于C++模板与非模板函数重载
https://mrbeancpp.github.io/2022/04/26/关于C-模板与非模板函数重载/
作者
MrBeanC
发布于
2022年4月26日
许可协议