繁体   English   中英

为什么C ++中的引用不是“const”?

[英]Why are references not “const” in C++?

我们知道“const变量”表示一旦分配,就无法更改变量,如下所示:

int const i = 1;
i = 2;

上面的程序将无法编译; gcc提示错误:

assignment of read-only variable 'i'

没问题,我能理解,但以下例子超出了我的理解:

#include<iostream>
using namespace std;
int main()
{
    boolalpha(cout);
    int const i = 1;
    cout << is_const<decltype(i)>::value << endl;
    int const &ri = i;
    cout << is_const<decltype(ri)>::value << endl;
    return 0;
}

它输出

true
false

奇怪的。 我们知道,一旦引用绑定到名称/变量,我们就无法更改此绑定,我们更改其绑定对象。 所以我想ri的类型应该与i相同:当i是一个int const ,为什么ri不是const

为什么“ri”不是“const”?

std::is_const检查类型是否为const限定。

如果T是const限定类型(即const或const volatile),则提供成员常量值等于true。 对于任何其他类型,值为false。

但是引用不能是const限定的。 参考文献[dcl.ref] / 1

除非通过使用typedef-name([dcl.typedef],[temp.param])或decltype-specifier([dcl.type.simple])引入cv-qualifiers,否则Cv限定的引用是不正确的。 ,在这种情况下,cv限定符被忽略。

所以is_const<decltype(ri)>::value将返回false becuase ri (引用)不是const限定类型。 正如你所说,我们不能在初始化后重新引用引用,这意味着引用总是“const”,另一方面,const限定引用或const不合格引用实际上可能没有意义。

这看似违反直觉,但我认为理解这一点的方法是要意识到,在某些方面, 引用句法上被视为指针

这似乎是指针的逻辑:

int main()
{
    boolalpha(cout);

    int const i = 1;
    cout << is_const<decltype(i)>::value << endl;

    int const* ri = &i;
    cout << is_const<decltype(ri)>::value << endl;
}

输出:

true
false

这是合乎逻辑的,因为我们知道它不是指针对象是const(它可以指向别处)它是被指向的对象。

所以我们正确地看到指针本身的常量返回为false

如果我们想让指针本身为const我们必须说:

int main()
{
    boolalpha(cout);

    int const i = 1;
    cout << is_const<decltype(i)>::value << endl;

    int const* const ri = &i;
    cout << is_const<decltype(ri)>::value << endl;
}

输出:

true
true

所以我认为我们看到了与参考文献的句法类比。

但是, 引用在语义上与指针不同,特别是在一个关键方面,我们不允许在绑定后重新绑定对另一个对象的引用。

因此即使引用指针共享相同的语法,规则也是不同的,因此语言阻止我们像这样声明引用本身const

int main()
{
    boolalpha(cout);

    int const i = 1;
    cout << is_const<decltype(i)>::value << endl;

    int const& const ri = i; // COMPILE TIME ERROR!
    cout << is_const<decltype(ri)>::value << endl;
}

我假设我们不允许这样做,因为当语言规则阻止引用以与指针相同的方式反弹时(如果它没有被声明为const ),似乎不需要它。

所以回答这个问题:

问)为什么“引用”在C ++中不是“const”?

在您的示例中,语法使得引用const的内容与声明指针时的方式相同。

无论是对还是错,我们都不允许将引用本身作为const但如果我们是这样,它将如下所示:

int const& const ri = i; // not allowed

Q)我们知道一旦引用绑定到名称/变量,我们就无法更改此绑定,我们更改其绑定对象。 所以我认为ri的类型应该与i相同:当i是一个int const ,为什么ri不是const

为什么decltype()没有转移到引用绑定的对象?

我想这是与指针的语义等价,也许decltype() (声明类型)的功能是回顾绑定发生之前声明的内容。

您需要使用std::remove_reference来获取您正在寻找的值。

std::cout << std::is_const<std::remove_reference<decltype(ri)>::type>::value << std::endl;

有关更多信息,请参阅此帖子

为什么宏不是const 功能? 文字? 类型的名称?

const事物只是不可变事物的一个子集。

由于引用类型只是 - 类型 - 它可能在某些意义上要求它们上的const -qualifier全部用于与其他类型(特别是指针类型)的对称,但这将非常繁琐非常繁琐。

如果C ++有不可变对象在默认情况下,要求mutable上的任何关键字,你不想成为const ,那么这本来是很容易:根本就没有允许程序员添加mutable引用类型。

事实上,它们是不可改变的。

并且,由于它们不是const -qualified,因此对于引用类型的is_const ,产生true会更加困惑。

我发现这是一个合理的妥协,特别是因为不变性无论如何都是由于没有语法来改变引用而强制执行的。

这是C ++中的一个怪癖/特性。 虽然我们不认为引用是类型,但它们实际上“坐”在类型系统中。 虽然这看起来很尴尬(假设在使用引用时,引用语义自动发生并且引用“不在路上”),有一些可辩护的原因可以解释为什么引用在类型系统中建模而不是作为一个单独的属性。类型。

首先,让我们考虑并非声明名称的每个属性都必须在类型系统中。 从C语言来看,我们有“存储类”和“链接”。 名称可以作为extern const int ri引入,其中extern表示静态存储类和链接的存在。 类型只是const int

C ++显然接受了表达式具有类型系统之外的属性的概念。 该语言现在具有“价值类”的概念,该概念试图组织表达式可以展示的越来越多的非类型属性。

但参考文献是类型。 为什么?

它可以用来在象一个声明C ++教程加以解释const int &ri引入ri为具有类型const int ,但是参考语义。 那个引用语义不是一个类型; 它只是一种属性,表示名称和存储位置之间的异常关系。 此外,引用不是类型的事实被用于合理化为什么你不能基于引用构造类型,即使类型构造语法允许它。 例如,引用的数组或指针是不可能的: const int &ari[5]const int &*pri

但实际上引用类型,因此decltype(ri)检索一些不合格的引用类型节点。 您必须在类型树中经过此节点,才能使用remove_reference基础类型。

当你使用ri ,引用会被透明地解析,因此ri “看起来和感觉就像i ”,并且可以被称为“别名”。 但是在类型系统中, ri实际上有一个“ 引用 const int ”的类型。

为什么引用类型?

考虑如果引用不是类型,那么这些函数将被认为具有相同的类型:

void foo(int);
void foo(int &);

这根本不是出于几乎不言而喻的原因。 如果它们具有相同的类型,则意味着任一声明都适用于任一定义,因此必须怀疑每个(int)函数都需要引用。

类似地,如果引用不是类型,那么这两个类声明将是等效的:

class foo {
  int m;
};

class foo {
  int &m;
};

一个翻译单元使用一个声明,同一个程序中的另一个翻译单元使用另一个声明是正确的。

事实是, 引用意味着实现上的差异,并且不可能将它与类型分开,因为C ++中的类型与实体的实现有关:它的“布局”可以说是位。 如果两个函数具有相同的类型,则可以使用相同的二进制调用约定调用它们:ABI是相同的。 如果两个结构或类具有相同的类型,则它们的布局与访问所有成员的语义相同。 引用的存在改变了类型的这些方面,因此将它们合并到类型系统中是一个简单的设计决策。 (但是,请注意这里的反驳:结构/类成员可以是static ,也可以更改表示形式;但这不是类型!)

因此,类型系统中的引用称为“第二类公民”(与ISO C中的函数和数组不同)。 有些事情我们不能用引用“做”,例如声明指向引用的指针或它们的数组。 但这并不意味着它们不是类型。 它们不是一种有意义的类型。

并非所有这些二等限制都是必不可少的。 鉴于存在引用结构,可能存在引用数组! 例如

// fantasy syntax
int x = 0, y = 0;
int &ar[2] = { x, y };

// ar[0] is now an alias for x: could be useful!

这只是在C ++中实现的,就是这样。 但是,引用的指针根本没有意义,因为从引用中提取的指针只会转到引用的对象。 没有引用数组的可能原因是C ++人员认为数组是一种从C继承的低级特性,它在很多方面都是无法修复的,并且它们不希望触及数组作为什么新的基础。 但是,引用数组的存在将清楚地说明引用必须是何种类型。

const -qualifiable类型:在ISO C90发现呢!

一些答案暗示了引用不采用const限定符这一事实。 这是一个红色的鲱鱼,因为声明const int &ri = i甚至没有尝试进行const限定的引用:它是对const限定类型(它本身不是const )的引用。 就像const in *ri声明指向某个const的指针一样,但该指针本身不是const

也就是说,引用本身不能携带const限定符。

然而,这并不是那么奇怪。 即使在ISO C 90语言中,并非所有类型都可以是const 即,数组不能。

首先,声明const数组的语法不存在: int a const [42]是错误的。

但是,上述声明尝试做的事情可以通过中间typedef来表达:

typedef int array_t[42];
const array_t a;

但这并不像它看起来那样做。 在这个声明中,它不是a获得const限定的元素,而是元素! 也就是说, a[0]是一个const int ,但是a只是“int的数组”。 因此,这不需要诊断:

int *p = a; /* surprise! */

这样做:

a[0] = 1;

同样,这强调了这样一种观点,即类型系统中的引用在某种意义上是“第二类”,就像数组一样。

请注意类比如何更深入,因为数组也具有“不可见的转换行为”,如引用。 如果程序员不必使用任何显式运算符,则标识符a自动转换为int *指针,就好像使用了表达式&a[0]一样。 这类似于当我们使用它作为主表达式时,参考ri如何神奇地表示它所绑定的对象i 它只是另一个“衰变”,就像“指针衰减的数组”。

就像我们不能被“数组到指针”衰变所迷惑而错误地认为“数组只是C和C ++中的指针”一样,我们同样不能认为引用只是没有自己类型的别名。

decltype(ri)抑制通常将引用转换为其引用对象时,这sizeof a抑制数组到指针转换的sizeof a并没有太大差别,并且对数组类型本身进行操作以计算其大小。

const X&x“表示X对象为X对象,但不能通过x更改该X对象。

并查看std :: is_const

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM