Programing

C ++에서 참조가 "const"가 아닌 이유는 무엇입니까?

lottogame 2020. 9. 16. 08:21
반응형

C ++에서 참조가 "const"가 아닌 이유는 무엇입니까?


"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?


이것은 직관적이지 않은 것처럼 보일 수 있지만 이것을 이해하는 방법은 특정 측면에서 참조구문 론적으로 포인터 처럼 취급 된다는 것을 깨닫는 것입니다 .

이것은 포인터에 대해 논리적으로 보입니다 .

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

그리고 나는 우리가 가진 구문 비유 볼 생각 때문에 참조 .

그러나 참조 는 특히 한 가지 중요한 측면 에서 포인터와 의미가 다르므로 바인딩 된 후에는 다른 개체에 대한 참조를 다시 바인딩 할 수 없습니다 .

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).

So to answer the question:

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

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?

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

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.


why is "ri" not "const"?

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

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.

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

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.

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.


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

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

For more information, see this post.


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.


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.

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++ 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.

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.

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.

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".

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.

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!)

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.

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.

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.

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

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

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

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:

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".

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.

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.


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

And see std::is_const.

참고URL : https://stackoverflow.com/questions/38044834/why-are-references-not-const-in-c

반응형