添加重载更改选择的重载
Adding overload changes which overload is selected
我试图了解重载选择规则如何导致以下(不直观(行为。当我有以下功能时:
#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&)
都同样专业,但重载 2 有一个空的参数包,因此选择了重载 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 匹配得更好,因为重载 2int&
参数类型的 cv 限定符是重载 0:const int&
之一的子集。
因此,正如Clang报道的那样,该电话是模棱两可的。
为了简化起见,在比较两个函数时,这里分 4 个步骤评估最佳可行函数:
- 检查哪个是最佳转换序列(此处的所有转换都是身份转换(,然后如果转换排名相同: 检查两个引用绑定
- 之间一个是否比另一个好,那么如果一个不是引用绑定或两个绑定不可微分, 检查其中一个函数是否是模板专用化,
- 另一个不是,则检查这两个函数是否是模板专用化
- 检查其中一个专业是否比另一个专业更专业。
- 添加重载更改选择的重载
- 具有参数包和通用引用的重载选择
- 重载模板函数未为特定类型选择正确的版本
- 为什么选择转换运算符的重载?
- 为什么传递文字 3 会选择 int 重载而不是短重载?
- 模板函数重载(泛型类型与模板模板类型)选择正确的重载
- 为什么<iostream>运算符<<会选择明显错误的重载?
- C++:使用重载而不是动态强制转换通过基选择派生类
- 模板实例化失败:编译器选择不正确的重载函数
- 编译器选择错误的重载函数
- 为什么重载分辨率不选择模板函数的 std::vector 重载?
- 如何获取通过重载分辨率选择的函子签名
- 一种类型特征,标识哪个类提供通过重载解析选择的函数
- 当参数不同时,重载分辨率不会选择模板
- 为什么 lambda 自动和参数选择常量重载?
- 方括号和圆括号操作器,如何选择重载
- 常量表达式来自重载函数的选择
- 强制编译器选择常量运算符重载
- 是否在重载选择期间未考虑默认参数的转换
- 构造函数重载选择了强制转换运算符而不是结构类型