为什么在c++中引用不是“const”?

时间:2021-11-09 18:51:59

We know that a "const variable" indicates that once assigned, you cannot change the variable, like this:

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

int const i = 1;
i = 2;

The program above will fail to compile; gcc prompts with an error:

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

assignment of read-only variable 'i'

No problem, I can understand it, but the following example is beyond my understanding:

没问题,我能理解,但是下面这个例子超出了我的理解范围:

#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;
}

It outputs

它输出

true
false

Weird. We know that once a reference is bound to a name/variable, we cannot change this binding, we change its bound object. So I suppose the type of ri should be the same as i: when i is an int const, why is ri not const?

奇怪。我们知道一旦引用被绑定到一个名称/变量,我们就不能改变这个绑定,我们就会改变它的绑定对象。所以我想ri的类型应该和I一样:当我是int const时,为什么ri不是const?

6 个解决方案

#1


49  

This may seem counter-intuitive but I think the way to understand this is to realize that, in certain respects, references are treated syntactically like pointers.

这似乎与直觉相反,但我认为理解这一点的方法是认识到,在某些方面,引用被语法地视为指针。

This seems logical for a pointer:

这对于指针来说似乎是合理的:

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;
}

Output:

输出:

true
false

This is logical because we know it is not the pointer object that is const (it can be made to point elsewhere) it is the object that is being pointed to.

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

So we correctly see the constness of the pointer itself returned as false.

因此,我们可以正确地看到指针本身的一致性返回为false。

If we want to make the pointer itself const we have to say:

如果我们想让指针本身

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;
}

Output:

输出:

true
true

And so I think we see a syntactic analogy with the reference.

所以我认为我们看到了一个与引用的句法类比。

However references are semantically different to pointers especially in one crucial respect, we are not allowed to rebind a reference to another object once bound.

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

So even though references share the same syntax as pointers the rules are different and so the language prevents us from declaring the reference itself const like this:

所以即使引用与指针有相同的语法规则是不同的,所以语言阻止我们像这样声明引用本身

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;
}

I assume we are not allowed to do this because it doesn't appear to be needed when the language rules prevent the reference from being rebound in the same way a pointer could(if it is not declared const).

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

So to answer the question:

回答这个问题

Q) Why “reference” is not a “const” in C++?

为什么“引用”在c++中不是“const”?

In your example the syntax makes the thing being referred to const the same way it would if you were declaring a pointer.

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

Rightly or wrongly we are not allowed to make the reference itself const but if we were it would look like this:

无论对还是错,我们都不允许引用它本身,但如果我们这样做,它就会是这样的:

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

Q) we know once a reference is bind to a name/variable, we cannot change this binding, we change its binded object. So I suppose the type of ri should be same as i: when i is a int const, why ri is not const?

Q)我们知道一旦引用被绑定到一个名称/变量,我们不能改变这个绑定,我们要改变它的绑定对象。所以我想ri的类型应该和I一样:当I是int const时,为什么ri不是const?

Why is the decltype() not transferred to the object the referece is bound to?

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

I suppose this is for semantic equivalence with pointers and maybe also the function of decltype() (declared type) is to look back at what was declared before the binding took place.

我认为这是为了实现与指针的语义等价,可能decltype()(声明类型)的功能就是在绑定发生之前查看声明的内容。

#2


50  

why is "ri" not "const"?

为什么"ri"不是"const"?

std::is_const checks whether the type is const-qualified or not.

is_const检查类型是否为const-qualified。

If T is a const-qualified type (that is, const, or const volatile), provides the member constant value equal true. For any other type, value is false.

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

But the reference can't be const-qualified. $8.3.2/1 References [dcl.ref]

但是,引用不能是合格的。$ 8.3.2/1引用[dcl.ref]

Cv-qualified references are ill-formed except when the cv-qualifiers are introduced through the use of a typedef-name ([dcl.typedef], [temp.param]) or decltype-specifier ([dcl.type.simple]), in which case the cv-qualifiers are ignored.

除了使用typedef-name ([dcl])引入cv-qualifier时,Cv-qualified引用的形式是不恰当的。[typedef]、[temp.param])或decltype-specifier ([dcl.type.simple]),在这种情况下,cv-qualifier被忽略。

So is_const<decltype(ri)>::value will return false becuase ri (the reference) is not a const-qualified type. As you said, we can't rebind a reference after initialization, which implies reference is always "const", on the other hand, const-qualified reference or const-unqualified reference might not make sense actually.

因此is_const ::value会返回false,因为这个引用不是const-qualified类型。正如您所说,我们不能在初始化后重新绑定引用,这意味着引用总是“const”,另一方面,const-qualified引用或const- reference实际上可能没有意义。 (ri)>

#3


18  

You need to use std::remove_reference for get the value you're looking for.

您需要使用std::remove_reference来获取您要查找的值。

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

For more information, see this post.

有关更多信息,请参阅本文。

#4


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.

由于引用类型只是-类型-可能需要在它们上使用const-qualifier来与其他类型(特别是指针类型)对称,但是这很快就会变得非常乏味。

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.

如果c++默认拥有不可变对象,在任何您不希望成为const的东西上都需要使用mutablec关键字,那么这将很容易:只是不允许程序员向引用类型添加mutablecopy。

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.

而且,由于它们不是const限定的,所以对于引用类型上的is_const来说,返回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.

我发现这是一种合理的妥协,特别是因为无论如何,不变性都是由于没有任何语法存在来改变引用。

#5


5  

This is a quirk/feature in C++. Although we don't think of references as types, they in fact "sit" in the type system. Although this seems awkward (given that when references are used, the reference semantics occurs automatically and the reference "gets out of the way"), there are some defensible reasons why references are modeled in the type system instead of as a separate attribute outside of type.

这是c++的一个特性。虽然我们不认为引用是类型,但它们实际上是类型系统中的“sit”。尽管这看起来很尴尬(考虑到在使用引用时,引用语义是自动出现的,引用“不碍事”),但是对于为什么引用在类型系统中建模而不是作为类型之外的单独属性,有一些合理的原因。

Firstly, let us consider that not every attribute of a declared name must be in the type system. From the C language, we have "storage class", and "linkage". A name can be introduced as extern const int ri, where the extern indicates static storage class and the presence of linkage. The type is just const int.

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

C++ obviously embraces the notion that expressions have attributes that are outside of the type system. The language now has a concept of "value class" which is an attempt to organize the growing number of non-type attributes that an expression can exhibit.

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

Yet references are types. Why?

然而,引用类型。为什么?

It used to be explained in C++ tutorials that a declaration like const int &ri introduced ri as having type const int, but reference semantics. That reference semantics was not a type; it was simply a kind of attribute indicating an unusual relationship between the name and the storage location. Furthermore, the fact that references are not types was used to rationalize why you cannot construct types based on references, even though the type construction syntax allows it. For instance, arrays or pointers to references not being possible: const int &ari[5] and const int &*pri.

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

But in fact references are types and so decltype(ri) retrieves some reference type node which is unqualified. You must descend past this node in the type tree to get to the underlying type with remove_reference.

但实际上引用是类型,所以decltype(ri)检索一些不合格的引用类型节点。您必须从类型树的这个节点下降到具有remove_reference的底层类型。

When you use ri, the reference is transparently resolved, so that ri "looks and feels like i" and can be called an "alias" for it. In the type system, though, ri does in fact have a type which is "reference to const int".

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

Why are references types?

Consider that if references were not types, then these functions would be considered to have the same type:

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

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

That simply cannot be for reasons which are pretty much self-evident. If they had the same type, that means either declaration would be suitable for either definition, and so every (int) function would have to be suspected of taking a reference.

这是不可能的,因为这是不言自明的。如果它们具有相同的类型,这意味着任何一个声明都适用于任何一个定义,因此每个(int)函数都必须被怀疑引用。

Similarly, if references weren't types, then these two class declarations would be equivalent:

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

class foo {
  int m;
};

class foo {
  int &m;
};

It would be correct for one translation unit to use one declaration, and another translation unit in the same program to use the other declaration.

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

The fact is that a reference implies a difference in implementation and it is impossible to separate that from type, because type in C++ has to do with the implementation of an entity: its "layout" in bits so to speak. If two functions have the same type, they can be invoked with the same binary calling conventions: the ABI is the same. If two structs or classes have the same type, their layout is the same as well as the semantics of access to all the members. The presence of references changes these aspects of types, and so it's a straightforward design decision to incorporate them into the type system. (However, note a counterargument here: a struct/class member can be static, which also changes the representation; yet that isn't type!)

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

Thus, references are in the type system as "second class citizens" (not unlike functions and arrays in ISO C). There are certain things we cannot "do" with references, such as declare pointers to references, or arrays of them. But that doesn't mean they aren't types. They just aren't types in a way that it makes sense.

因此,在类型系统中引用是“二等公民”(不像ISO C中的函数和数组)。但这并不意味着它们不是类型。它们并不是有意义的类型。

Not all these second-class-restrictions are essential. Given that there are structures of references, there could be arrays of references! 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!

This just isn't implemented in C++, that's all. Pointers to references do not make sense at all, though, because a pointer lifted from a reference just goes to the referenced object. The likely reason why there are no arrays of references is that the C++ people consider arrays to be a kind of low-level feature inherited from C that is broken in many ways that are irreparable, and they don't want to touch arrays as the basis for anything new. The existence of arrays of references, though, would make a clear example of how references have to be types.

这只是在c++中没有实现,仅此而已。指向引用的指针完全没有意义,因为从引用中提取的指针只指向引用的对象。没有引用数组的可能原因是c++的人们认为数组是一种从C继承而来的低级特性,这种特性在很多方面都是无法修复的,他们不希望将数组作为任何新事物的基础。不过,引用数组的存在可以清楚地说明引用必须是类型。

Non-const-qualifiable types: found in ISO C90, too!

Some answers are hinting at the fact that references don't take a const qualifier. That is rather a red herring, because the declaration const int &ri = i isn't even attempting to make a const-qualified reference: it's a reference to a const-qualified type (which is itself not const). Just like const in *ri declares a pointer to something const, but that pointer is itself not const.

一些答案暗示了这样一个事实,即引用没有使用const限定符。这完全是在转移注意力,因为声明const int &ri = i甚至没有尝试做一个const-qualified引用:它是对const-qualified类型的引用(它本身不是const)。就像*ri中的const声明了一个指向某个const的指针,但是该指针本身并不是const。

That said, it is true that references cannot carry the const qualifier themselves.

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

Yet, this is not so bizarre. Even in the ISO C 90 language, not all types can be const. Namely, arrays cannot be.

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

Firstly, the syntax doesn't exist for declaring a const array: int a const [42] is erroneous.

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

However, what the above declaration is trying to do can be expressed via an intermediate typedef:

然而,上述声明所要做的可以通过中间类型定义来表达:

typedef int array_t[42];
const array_t a;

But this doesn't do what it looks like it does. In this declaration, it is not a which gets const qualified, but the elements! That is to say, a[0] is a const int, but a is just "array of int". Consequently, this doesn't require a diagnostic:

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

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

This does:

这样做:

a[0] = 1;

Again, this underscores the idea that references are in some sense "second class" in the type system, like arrays.

同样,这也强调了引用在某种意义上是类型系统中的“第二类”,比如数组。

Note how the analogy holds even more deeply, since arrays also have an "invisible conversion behavior", like references. Without the programmer having to use any explicit operator, the identifier a automatically turns into an int * pointer, as if the expression &a[0] had been used. This is analogous to how a reference ri, when we use it as a primary expression, magically denotes the object i to which it is bound. It's just another "decay" like the "array to pointer decay".

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

And just like we must not become confused by the "array to pointer" decay into wrongly thinking that "arrays are just pointers in C and C++", we likewise mustn't think that references are just aliases that have no type of their own.

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

When decltype(ri) suppresses the usual conversion of the reference to its referent object, this is not so different from sizeof a suppressing the array-to-pointer conversion, and operating on the array type itself to calculate its size.

当decltype(ri)抑制对它的引用到它的引用对象的通常转换时,这和抑制arrayto -pointer转换的sizeof并对数组类型本身进行操作来计算它的大小没有什么不同。

#6


-5  

const X& x” means x aliases an X object, but you can’t change that X object via x.

" const x&x "是指x别名一个x对象,但不能通过x来改变那个x对象。

And see std::is_const.

看看std::is_const。

#1


49  

This may seem counter-intuitive but I think the way to understand this is to realize that, in certain respects, references are treated syntactically like pointers.

这似乎与直觉相反,但我认为理解这一点的方法是认识到,在某些方面,引用被语法地视为指针。

This seems logical for a pointer:

这对于指针来说似乎是合理的:

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;
}

Output:

输出:

true
false

This is logical because we know it is not the pointer object that is const (it can be made to point elsewhere) it is the object that is being pointed to.

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

So we correctly see the constness of the pointer itself returned as false.

因此,我们可以正确地看到指针本身的一致性返回为false。

If we want to make the pointer itself const we have to say:

如果我们想让指针本身

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;
}

Output:

输出:

true
true

And so I think we see a syntactic analogy with the reference.

所以我认为我们看到了一个与引用的句法类比。

However references are semantically different to pointers especially in one crucial respect, we are not allowed to rebind a reference to another object once bound.

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

So even though references share the same syntax as pointers the rules are different and so the language prevents us from declaring the reference itself const like this:

所以即使引用与指针有相同的语法规则是不同的,所以语言阻止我们像这样声明引用本身

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;
}

I assume we are not allowed to do this because it doesn't appear to be needed when the language rules prevent the reference from being rebound in the same way a pointer could(if it is not declared const).

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

So to answer the question:

回答这个问题

Q) Why “reference” is not a “const” in C++?

为什么“引用”在c++中不是“const”?

In your example the syntax makes the thing being referred to const the same way it would if you were declaring a pointer.

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

Rightly or wrongly we are not allowed to make the reference itself const but if we were it would look like this:

无论对还是错,我们都不允许引用它本身,但如果我们这样做,它就会是这样的:

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

Q) we know once a reference is bind to a name/variable, we cannot change this binding, we change its binded object. So I suppose the type of ri should be same as i: when i is a int const, why ri is not const?

Q)我们知道一旦引用被绑定到一个名称/变量,我们不能改变这个绑定,我们要改变它的绑定对象。所以我想ri的类型应该和I一样:当I是int const时,为什么ri不是const?

Why is the decltype() not transferred to the object the referece is bound to?

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

I suppose this is for semantic equivalence with pointers and maybe also the function of decltype() (declared type) is to look back at what was declared before the binding took place.

我认为这是为了实现与指针的语义等价,可能decltype()(声明类型)的功能就是在绑定发生之前查看声明的内容。

#2


50  

why is "ri" not "const"?

为什么"ri"不是"const"?

std::is_const checks whether the type is const-qualified or not.

is_const检查类型是否为const-qualified。

If T is a const-qualified type (that is, const, or const volatile), provides the member constant value equal true. For any other type, value is false.

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

But the reference can't be const-qualified. $8.3.2/1 References [dcl.ref]

但是,引用不能是合格的。$ 8.3.2/1引用[dcl.ref]

Cv-qualified references are ill-formed except when the cv-qualifiers are introduced through the use of a typedef-name ([dcl.typedef], [temp.param]) or decltype-specifier ([dcl.type.simple]), in which case the cv-qualifiers are ignored.

除了使用typedef-name ([dcl])引入cv-qualifier时,Cv-qualified引用的形式是不恰当的。[typedef]、[temp.param])或decltype-specifier ([dcl.type.simple]),在这种情况下,cv-qualifier被忽略。

So is_const<decltype(ri)>::value will return false becuase ri (the reference) is not a const-qualified type. As you said, we can't rebind a reference after initialization, which implies reference is always "const", on the other hand, const-qualified reference or const-unqualified reference might not make sense actually.

因此is_const ::value会返回false,因为这个引用不是const-qualified类型。正如您所说,我们不能在初始化后重新绑定引用,这意味着引用总是“const”,另一方面,const-qualified引用或const- reference实际上可能没有意义。 (ri)>

#3


18  

You need to use std::remove_reference for get the value you're looking for.

您需要使用std::remove_reference来获取您要查找的值。

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

For more information, see this post.

有关更多信息,请参阅本文。

#4


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.

由于引用类型只是-类型-可能需要在它们上使用const-qualifier来与其他类型(特别是指针类型)对称,但是这很快就会变得非常乏味。

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.

如果c++默认拥有不可变对象,在任何您不希望成为const的东西上都需要使用mutablec关键字,那么这将很容易:只是不允许程序员向引用类型添加mutablecopy。

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.

而且,由于它们不是const限定的,所以对于引用类型上的is_const来说,返回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.

我发现这是一种合理的妥协,特别是因为无论如何,不变性都是由于没有任何语法存在来改变引用。

#5


5  

This is a quirk/feature in C++. Although we don't think of references as types, they in fact "sit" in the type system. Although this seems awkward (given that when references are used, the reference semantics occurs automatically and the reference "gets out of the way"), there are some defensible reasons why references are modeled in the type system instead of as a separate attribute outside of type.

这是c++的一个特性。虽然我们不认为引用是类型,但它们实际上是类型系统中的“sit”。尽管这看起来很尴尬(考虑到在使用引用时,引用语义是自动出现的,引用“不碍事”),但是对于为什么引用在类型系统中建模而不是作为类型之外的单独属性,有一些合理的原因。

Firstly, let us consider that not every attribute of a declared name must be in the type system. From the C language, we have "storage class", and "linkage". A name can be introduced as extern const int ri, where the extern indicates static storage class and the presence of linkage. The type is just const int.

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

C++ obviously embraces the notion that expressions have attributes that are outside of the type system. The language now has a concept of "value class" which is an attempt to organize the growing number of non-type attributes that an expression can exhibit.

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

Yet references are types. Why?

然而,引用类型。为什么?

It used to be explained in C++ tutorials that a declaration like const int &ri introduced ri as having type const int, but reference semantics. That reference semantics was not a type; it was simply a kind of attribute indicating an unusual relationship between the name and the storage location. Furthermore, the fact that references are not types was used to rationalize why you cannot construct types based on references, even though the type construction syntax allows it. For instance, arrays or pointers to references not being possible: const int &ari[5] and const int &*pri.

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

But in fact references are types and so decltype(ri) retrieves some reference type node which is unqualified. You must descend past this node in the type tree to get to the underlying type with remove_reference.

但实际上引用是类型,所以decltype(ri)检索一些不合格的引用类型节点。您必须从类型树的这个节点下降到具有remove_reference的底层类型。

When you use ri, the reference is transparently resolved, so that ri "looks and feels like i" and can be called an "alias" for it. In the type system, though, ri does in fact have a type which is "reference to const int".

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

Why are references types?

Consider that if references were not types, then these functions would be considered to have the same type:

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

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

That simply cannot be for reasons which are pretty much self-evident. If they had the same type, that means either declaration would be suitable for either definition, and so every (int) function would have to be suspected of taking a reference.

这是不可能的,因为这是不言自明的。如果它们具有相同的类型,这意味着任何一个声明都适用于任何一个定义,因此每个(int)函数都必须被怀疑引用。

Similarly, if references weren't types, then these two class declarations would be equivalent:

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

class foo {
  int m;
};

class foo {
  int &m;
};

It would be correct for one translation unit to use one declaration, and another translation unit in the same program to use the other declaration.

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

The fact is that a reference implies a difference in implementation and it is impossible to separate that from type, because type in C++ has to do with the implementation of an entity: its "layout" in bits so to speak. If two functions have the same type, they can be invoked with the same binary calling conventions: the ABI is the same. If two structs or classes have the same type, their layout is the same as well as the semantics of access to all the members. The presence of references changes these aspects of types, and so it's a straightforward design decision to incorporate them into the type system. (However, note a counterargument here: a struct/class member can be static, which also changes the representation; yet that isn't type!)

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

Thus, references are in the type system as "second class citizens" (not unlike functions and arrays in ISO C). There are certain things we cannot "do" with references, such as declare pointers to references, or arrays of them. But that doesn't mean they aren't types. They just aren't types in a way that it makes sense.

因此,在类型系统中引用是“二等公民”(不像ISO C中的函数和数组)。但这并不意味着它们不是类型。它们并不是有意义的类型。

Not all these second-class-restrictions are essential. Given that there are structures of references, there could be arrays of references! 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!

This just isn't implemented in C++, that's all. Pointers to references do not make sense at all, though, because a pointer lifted from a reference just goes to the referenced object. The likely reason why there are no arrays of references is that the C++ people consider arrays to be a kind of low-level feature inherited from C that is broken in many ways that are irreparable, and they don't want to touch arrays as the basis for anything new. The existence of arrays of references, though, would make a clear example of how references have to be types.

这只是在c++中没有实现,仅此而已。指向引用的指针完全没有意义,因为从引用中提取的指针只指向引用的对象。没有引用数组的可能原因是c++的人们认为数组是一种从C继承而来的低级特性,这种特性在很多方面都是无法修复的,他们不希望将数组作为任何新事物的基础。不过,引用数组的存在可以清楚地说明引用必须是类型。

Non-const-qualifiable types: found in ISO C90, too!

Some answers are hinting at the fact that references don't take a const qualifier. That is rather a red herring, because the declaration const int &ri = i isn't even attempting to make a const-qualified reference: it's a reference to a const-qualified type (which is itself not const). Just like const in *ri declares a pointer to something const, but that pointer is itself not const.

一些答案暗示了这样一个事实,即引用没有使用const限定符。这完全是在转移注意力,因为声明const int &ri = i甚至没有尝试做一个const-qualified引用:它是对const-qualified类型的引用(它本身不是const)。就像*ri中的const声明了一个指向某个const的指针,但是该指针本身并不是const。

That said, it is true that references cannot carry the const qualifier themselves.

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

Yet, this is not so bizarre. Even in the ISO C 90 language, not all types can be const. Namely, arrays cannot be.

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

Firstly, the syntax doesn't exist for declaring a const array: int a const [42] is erroneous.

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

However, what the above declaration is trying to do can be expressed via an intermediate typedef:

然而,上述声明所要做的可以通过中间类型定义来表达:

typedef int array_t[42];
const array_t a;

But this doesn't do what it looks like it does. In this declaration, it is not a which gets const qualified, but the elements! That is to say, a[0] is a const int, but a is just "array of int". Consequently, this doesn't require a diagnostic:

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

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

This does:

这样做:

a[0] = 1;

Again, this underscores the idea that references are in some sense "second class" in the type system, like arrays.

同样,这也强调了引用在某种意义上是类型系统中的“第二类”,比如数组。

Note how the analogy holds even more deeply, since arrays also have an "invisible conversion behavior", like references. Without the programmer having to use any explicit operator, the identifier a automatically turns into an int * pointer, as if the expression &a[0] had been used. This is analogous to how a reference ri, when we use it as a primary expression, magically denotes the object i to which it is bound. It's just another "decay" like the "array to pointer decay".

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

And just like we must not become confused by the "array to pointer" decay into wrongly thinking that "arrays are just pointers in C and C++", we likewise mustn't think that references are just aliases that have no type of their own.

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

When decltype(ri) suppresses the usual conversion of the reference to its referent object, this is not so different from sizeof a suppressing the array-to-pointer conversion, and operating on the array type itself to calculate its size.

当decltype(ri)抑制对它的引用到它的引用对象的通常转换时,这和抑制arrayto -pointer转换的sizeof并对数组类型本身进行操作来计算它的大小没有什么不同。

#6


-5  

const X& x” means x aliases an X object, but you can’t change that X object via x.

" const x&x "是指x别名一个x对象,但不能通过x来改变那个x对象。

And see std::is_const.

看看std::is_const。