c++ - 添加重载更改选择了哪个重载
问题描述
我试图了解重载选择规则如何导致以下(不直观的)行为。当我具有以下功能时:
#include <iostream>
// Overload 1
template<class T>
void write(T data)
{
std::cout << "Called write(T data)" << std::endl;
}
// Overload 2
template<class T, class ...U>
void write(T&& obj, U&&... objs)
{
std::cout << "Called write(T&& obj, U&&... objs)" << std::endl;
}
int main(int, char**)
{
int j = 0;
write(j);
return 0;
}
选择void write(T data)
过载(过载 1)。我认为这对我来说很有意义:重载选择的候选者是void write<T>(T)
T = int
和void write<T,U>(T&)
T = int, U = <>
。两者write(T)
和write(T&)
都将同样专业化,但 Overload 2 有一个空参数包,因此选择了 Overload 1。但是,如果我添加第三个重载:
#include <iostream>
// Overload 0
void write(const int& data)
{
std::cout << "Called write(const int& data)" << std::endl;
}
// Overload 1
template<class T>
void write(T data)
{
std::cout << "Called write(T data)" << std::endl;
}
// Overload 2
template<class T, class ...U>
void write(T&& obj, U&&... objs)
{
std::cout << "Called write(T&& obj, U&&... objs)" << std::endl;
}
int main(int, char**)
{
int j = 0;
write(j);
return 0;
}
然后突然void write(T&& obj, U&&... objs)
(过载 2)被调用。为什么添加没有被选中的重载会改变实际选中的重载?
如果唯一的候选人是void write<T,U>(T&)
T = int, U = <>
并且void write(const int&)
我理解为什么void write<T,U>(T&)
会被选中,那么添加额外超载可能会阻止void write(T data)
参与超载选择?如果是,为什么?
由于这似乎是编译器特定的行为,因此在 gcc 7.3.0 上观察到了这一点。
一些更有趣的行为:如果函数被重新排序,使得新的重载被放置在原来的两个之间(即重载 1,然后是重载 0,然后是重载 2)然后 gcc 用call of overloaded ‘write(int&)’ is ambiguous
. 如果函数被重新排序使得新的重载在最后(即,重载 1,然后是重载 2,然后是重载 0)write(const int& data)
则被选中。
解决方案
我认为这是一个 GCC 错误:
重载是:
- 过载 0:
write(const int&)
- 过载1:
write(T) [T=int] -> write(int)
- 过载2:
write(T&&,U&&...) [T=int&,U=[]] -> write(int&)
重载 0 比重载 1 更匹配,因为重载 0 不是模板函数特化。
重载 1 比重载 2 更匹配,因为重载 1 是一个比重载 2 更专业的函数模板。
重载 2 比重载 0 更匹配,因为重载 2 的参数类型的 cv 限定符int&
是重载 0: 之一的子集const int&
。
因此,正如 Clang 所报告的那样,该调用是模棱两可的。
为简化起见,在比较两个函数时,最佳可行函数在此处分 4 步进行评估:
- 检查哪个是最好的转换顺序(这里所有的转换都是恒等转换),然后如果转换等级相同:
- 检查两个引用绑定之间是否一个比另一个更好,然后如果一个不是引用绑定或者两个绑定不可区分,
- 检查一个函数是否是模板特化而另一个不是,那么这两个函数是否是模板特化
- 检查其中一个专业是否比另一个专业更专业。
推荐阅读
- ios-simulator - 使用模拟器触发重大位置更改事件的具体方法是什么?
- c# - 在 Unity 中读取串行端口“COM3”。连接建立成功,但未调用DataReceiveHandler
- javascript - 在等待之后调用 setState 时,状态立即可用
- pandas - 为什么这个追星族不能总结价格?
- rpm - 如何在规范文件中使用 $RPM_INSTALL_PREFIX?
- python - 如何通过指定初始和最终点通过 3d 矩阵(ndarray 3-dim)提取线轮廓(光线跟踪线)
- python - 从熊猫数据框中提取无向图
- python - 如何每周自动将数据添加到数据库?
- java - 如何使用 Maven 将依赖文件、放入目标中包含到 Java 代码中?
- python - 如何在 Python 中编写具有 sudo 权限的文件?