请考虑以下事项:
struct A {
A(float ) { }
A(int ) { }
};
int main() {
A{1.1}; // error: ambiguous
}
无法编译,但有关A::A
的模糊重载的错误。两位候选人都被认为是可行的,因为requirement is simply:
其次,要使
F
成为可行的函数,每个参数都应存在一个隐式转换序列(13.3.3.1),它将该参数转换为F
的相应参数。
虽然存在从double
到int
的隐式转换序列,但A(int )
重载不是实际上可行的(在规范的非C ++中) - 标准意义) - 这将涉及缩小转换,因此是不正确的。
为什么在确定可行的候选人的过程中不考虑缩小转换?尽管只有一个候选人可行,但是有没有其他情况下过载被认为是模棱两可的?
答案 0 :(得分:15)
问题在于可以根据类型检测到缩小转换的事实。
在C ++中,有很复杂的方法可以在编译时生成值。
阻止缩小转化率是一件好事。使C ++的重载分辨率比现在更复杂是一件坏事。
在确定重载决策时忽略缩小转换规则(这使得重载决策纯粹与类型有关),然后在选定的重载导致缩小转换时错误输出,使重载决策变得更加复杂,并添加了一种方法检测并防止缩小转换。
只有一个候选者可行的两个例子是在实例化期间“迟到”失败的模板函数,以及复制列表初始化(其中explicit
构造函数被考虑,但如果选择它们,则会出现错误)。同样,具有这种影响的重载分辨率会使重载决策变得比现在更加复杂。
现在,有人可能会问,为什么不将纯粹的转换缩小到类型系统?
缩小转换纯粹是基于类型的转换是不可行的。这些更改可能会破坏编译器可以证明有效的大量“遗留”代码。当大多数错误是实际错误时,扫描代码库所需的工作更有价值,而不是新编译器版本是一个混蛋。
unsigned char buff[]={0xff, 0x00, 0x1f};
这会在基于类型的缩小转换下失败,因为0xff
的类型为int
,而且此类代码非常常见。
如果这些代码需要对int
文字进行毫无意义的unsigned char
文字修改,那么我们设置一个标志就可以告诉编译器关闭愚蠢的错误。 / p>
答案 1 :(得分:8)
缩小是编译器只知道内置类型的东西。用户定义的隐式转换不能标记为缩小。
首先,不应允许缩小转换。 (不幸的是,这是C兼容性所必需的。在{}
初始化禁止缩小内置类型的情况下,这已得到一定程度的纠正。)
鉴于这些,过载规则无需提及这种特殊情况是有道理的。这可能是偶然的便利,但并不是那么有价值。 IMO通常会更好地减少重载决策所涉及的因素,并拒绝更多不明确的事情,迫使程序员明确地解决这些问题。
另外,当double不是常量表达式或double太大时,double to float是一个变窄的转换。
#include <iostream>
#include <iomanip>
int main() {
double d{1.1};
float f{d};
std::cout << std::setprecision(100) << d << " " << f << '\n';
}
这通常会产生错误:
main.cpp:7:13: error: non-constant-expression cannot be narrowed from type 'double' to 'float' in initializer list [-Wc++11-narrowing]
float f{d};
^