我读过这本书,书中写道,我们可以将一种类型的const引用分配给任何其他类型的对象,原因是,内部编译器将Rvalue分配给与引用相同类型的对象然后将const引用初始化为同一类型的对象。但是,如果这种类型的隐式转换有助于将const引用分配给不同类型的对象,那么为什么不可能隐式执行相同的转换,因为对于这种显式转换。
#include<iostream>
using namespace std;
int main()
{
int a = 10;
double temp = (double)a;
double &x = temp;
cout << x << endl;
return 0;
}
它的工作方式是一样的,为什么它没有在编译器中预先配置?
如果编译器必须执行从一种类型到另一种类型的隐式转换,这意味着它必须创建一个临时值来保存转换后的值。
非常量引用不能绑定到临时引用。时期
const引用可以,并且它将延长临时的生存期。
换句话说:
#include <iostream>
using namespace std;
int main()
{
int a = 10;
double &x = (double)a; // ERROR! Can't bind to the temporary double
cout << x << endl;
const double &x2 = (double)a; // OK! Binds to the temporary double
cout << x2 << endl;
return 0;
}
实时演示
简而言之,它可以帮助您避免错误。假设您可以将一个对象分配给另一种类型的非常数引用,如:
// DISCLAIMER: bad code; does not compile
int a = 10;
double &x = a;
x = 2.0;
这意味着什么?第二行表示x
是a
的别名,这意味着对x
的更改反映在a
中。如果您不打算这样暗示,那么非const
引用就不是合适的工具。(如果x
没有更改,请使用const
引用。如果更改不应反映在a
中,请使用副本而不是引用。(因此,第三行应将a
的值设置为2
。然而,它不能。
隐式转换很容易是单向的。可能没有反向转换。当然,在这个特定的例子中,将浮点
2.0
转换为整数2
是没有问题的,但这是一种特殊情况。当你看到更复杂的场景,特别是涉及类而不是基本类型的场景时,反向转换可能甚至没有意义。语言不能保证通过引用进行修改,因此为了一致性,在任何情况下都禁止进行修改。隐式转换涉及创建一个临时对象。如果编译行
const double &x = a;
,编译器将执行与代码相同的操作:创建一个新的float
对象,并让x
引用这个新对象。在编译器的版本中,float
对象没有名称,而在代码中,它被称为temp
。在您的代码中,如果您要尝试修改x
,则修改将出现在temp
中,但不会显示在a
中,因为它们是不同的对象。编译器的版本也是如此——如果您能够通过引用进行修改,那么您将修改临时对象,而不是原始对象。这打破了引用的语义。
结果是,如果您认为需要对不同类型(需要转换的对象(的非const
引用,那么您的逻辑可能有问题。编译器可以很快识别出这一点,并告诉你你正在尝试的东西不会像你想象的那样工作。这是一个没有已知效用的陷阱,所以这里有大的橙色圆锥体来抵御粗心的人。不要浪费时间测试可执行文件,因为已经发现了一个错误。
语言规则允许编译器在编译时指出这个几乎确定的错误,从而为您省去调试运行时症状的麻烦。