对引用进行类型转换

Type conversions on references

本文关键字:类型转换 引用      更新时间:2023-10-16

我发现const引用有一种意外行为:

#include <iostream>
using namespace std;
template <typename T>
void myfunction() {
    T b = 30;
    const int &i = b;
    cout << "Before change: " << i << endl;
    b = 33;
    cout << "After change: "  << i << endl;
}
int main() {
    cout << "int" << endl;
    myfunction<int>();
    cout << endl;
    cout << "double" << endl;
    myfunction<double>();
}

这给出了以下结果:

int
Before change: 30
After change: 33
double
Before change: 30
After change: 30

我知道在第二种情况下;我没有引用b,因为它是双精度的,但引用了一个临时对象。这也是为什么在更改b时i的值没有改变的原因。

但为什么这样的行为是被允许的呢?在我看来,这是违反直觉的。只使用const int i = b有什么性能优势吗?

你通常可以想到

const int &i = b;

作为

int const* const p_i = &b;

自动取消引用,比如(但不要太当真!)

#define i (*p_i)

第一个const表示不能通过指针/引用更改b。但你可以通过其他方式改变它。然后,您通过看到的指针/引用发生了更改。

b类型为double的情况下,引用不能直接引用它。然后,您得到的是从double转换而来的临时int,其生存期延长到引用的生存期(事物的指针视图有点崩溃!)。在这种情况下,对原始文件所做的更改不会反映在引用所指的临时文件中。


顺便说一句,这种混叠(两种或多种引用相同事物的方式,具有不同的限制)原则上是通过引用const来传递的问题。令人高兴的是,这在实践中并不是什么大问题。事实上,我从来没有遇到过这种情况,除了创建一些愚蠢的例子来教授技术可能性。


关于

"但为什么这样的行为是被允许的呢?

…这询问了两个问题:

  • 绑定到引用时临时的生存期扩展。

  • doubleint的隐式转换。

当我问Bjarne延长寿命的理由时,在过去,每个人(包括Bjanne)都使用comp.lang.c++Usenet组,他回答说,这主要是为了规则的一致性。

doubleint,以及通常从任何内置数字类型到另一种内置数字类型的潜在且通常破坏信息的隐式转换,在旧的C时代就存在。在C++11及更高版本中,您可以通过使用braked初始化程序来限制允许的转换。因此,如果你写

const int &i = {b};

…那么对于b类型为double的情况,代码就不会编译,因为这将是缩小转换

当然,这对例如键入short(不再是缩小转换范围)没有帮助,但它有一点帮助。

在这种情况下,我不同意这违反直觉。

考虑以下示例

struct A
{
    A( double x ) : x ( x ) {}
    double x;
};
const A &ra1 = A( 10.0 );
const A &ra2 = 10.0;

我想你们不会反对这些引用声明。它们是直观的。

现在只需将类名A替换为类型int,就可以得到

const int &ri1 = int( 10.0 );
const int &ri2 = 10.0;

在我看来,这是有逻辑的。

例如,MS VC++为最后一个声明生成警告

警告C4244:"initializing":从"double"转换为"const"int',可能的数据丢失

至于性能优势,您应该仔细查看生成的对象代码。它可以以这样的方式发生,即对于这两个声明,编译器生成相同的对象代码。