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

时间:2016-06-27 01:40:51

标签: c++ reference const language-lawyer decltype

我们知道“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相同:当iint const时,为什么ri不是const

6 个答案:

答案 0 :(得分:53)

  

为什么&#34; ri&#34;不是&#34; const&#34;?

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

  

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

但是引用不能是const限定的。 References [dcl.ref]/1

  除了cv限定符之外,

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

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

答案 1 :(得分:52)

这可能看似违反直觉,但我认为理解这一点的方法是要意识到,在某些方面,引用语法视为指针< / em>的

对于指针来说,这似乎是合乎逻辑的:

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(它可以指向别处)它是被指向的对象。

所以我们正确地看到指针本身的 constness 返回为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

所以我认为我们会看到与引用的语法类比。

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

因此即使引用指针具有相同的语法,规则也不同,因此语言阻止我们声明引用本身{ {1}}像这样:

const

我认为我们不允许这样做,因为当语言规则阻止引用以与指针<相同的方式反弹时,似乎不需要这样做/ em> can(如果未声明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; } )。

所以回答这个问题:

  

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

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

正确或错误地我们不允许引用本身const但是如果我们是这样的话:

const
  

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

为什么const未转移到 referece 绑定的对象?

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

答案 2 :(得分:18)

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

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

有关详细信息,请参阅this post

答案 3 :(得分:6)

Why are macros not const? Functions? Literals? The names of types?

const things are only a subset of immutable things.

Since reference types are just that — types — it may have made some sense to require the const-qualifier on them all for symmetry with other types (particularly with pointer types), but this would get very tedious very quickly.

If C++ had immutable objects by default, requiring the mutable keyword on anything you didn't want to be const, then this would have been easy: simply don't allow programmers to add mutable to reference types.

As it is, they are immutable without qualification.

And, since they are not const-qualified, it would probably be more confusing for is_const on a reference type to yield true.

I find this to be a reasonable compromise, especially since the immutability is anyway enforced by the mere fact that no syntax exists to mutate a reference.

答案 4 :(得分:5)

这是C ++中的一个怪癖/功能。虽然我们并没有将引用视为类型,但实际上它们并不适合#34;在类型系统中。虽然这看起来很尴尬(假设在使用引用时,引用语义自动发生并且引用&#34;偏离方式&#34;),有一些可辩护的原因,为什么引用在类型系统中建模而不是类型之外的单独属性。

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

C ++显然包含了表达式具有类型系统之外的属性的概念。该语言现在具有&#34;价值等级&#34;这是尝试组织表达式可以展示的越来越多的非类型属性。

然而参考是类型。为什么呢?

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

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

当您使用ri时,参考会被透明地解析,以便ri&#34;看起来像i&#34;并且可以称为&#34;别名&#34;为了它。但是,在类型系统中,ri实际上的类型是&#34; 引用 const int&#34;。

为什么是引用类型?

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

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

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

同样,如果引用不是类型,那么这两个类声明将是等价的:

class foo {
  int m;
};

class foo {
  int &m;
};

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

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

因此,引用在类型系统中作为&#34;二等公民&#34; (与ISO C中的函数和数组不同)。我们不能做某些事情&#34;做&#34;带引用,例如声明指向引用的指针或它们的数组。但这并不意味着它们不属于任何类型。它们不是一种有意义的类型。

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

// 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 - 合格类型:也可在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只是&#34; int&#34;的数组。因此,这不需要诊断:

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

这样做:

a[0] = 1;

同样,这强调了引用在某种意义上的观点&#34;第二类&#34;在类型系统中,如数组。

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

就像我们不能被&#34;数组混淆到指针&#34;歪曲错误地认为&#34;数组只是C和C ++中的指针&#34;,我们同样不能认为引用只是没有自己类型的别名。

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

答案 5 :(得分:-5)

const X&amp; x“表示x对X对象进行别名,但不能通过x更改该X对象。

并参阅 std::is_const