[c++] 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 인 포인터 객체 가 아니라 (다른 곳을 가리 키도록 만들 수 있음) 그것이 가리키는 객체 라는 것을 알고 있기 때문에 논리적 입니다.

그래서 우리는 올바르게 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

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

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

따라서 참조포인터 와 동일한 구문을 공유 하더라도 규칙이 다르므로 언어가 참조 자체 를 선언하지 못하도록 합니다.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없지만 만약 그렇다면 다음과 같을 것입니다.

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

Q) 참조가 이름 / 변수에 바인딩되면이 바인딩을 변경할 수 없으며 바인딩 된 개체를 변경한다는 것을 알고 있습니다. 따라서 유형은 다음과 ri같아야 한다고 가정합니다 i. when iis a int const, whyri 그렇지 const않습니까?

decltype() 하여 객체에 전송되지 referece가 에 바인딩?

나는 이것이 포인터 와 의미 론적 동등성을위한 decltype()것이라고 생각하고 아마도 (선언 된 유형) 의 기능은 바인딩이 일어나기 전에 선언 된 것을 되돌아 보는 것입니다.


답변

“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 한정자가 도입되는 경우를 제외하고 Cv 한정 참조는 형식이 잘못되었습니다. ,이 경우 cv 한정자는 무시됩니다.

그래서 is_const<decltype(ri)>::value돌아갑니다 falsebecuase ri(참조)는 const를 수식 형식이 아닙니다. 말했듯이 초기화 후에는 참조를 리 바인드 할 수 없습니다. 이는 참조가 항상 “const”임을 의미합니다. 반면에 const 한정 참조 또는 const 비 한정 참조는 실제로 의미가 없을 수 있습니다.


답변

원하는 std::remove_reference가치를 얻기 위해 사용해야 합니다.

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

자세한 내용은 이 게시물을 참조하십시오 .


답변

매크로가 아닌 이유는 무엇 const입니까? 기능? 리터럴? 유형의 이름?

const 사물은 불변의 일부일뿐입니다.

참조 유형은 바로 유형이기 때문에 const 다른 유형 (특히 포인터 유형의 경우)과의 대칭을 위해 모두에 대해 -qualifier 일 수 있지만 이것은 매우 지루할 것입니다.

C ++에 기본적으로 변경 불가능한 객체가 있고 원하지 않는mutable 항목에 대한 키워드가 필요 하다면 이것은 쉬웠을 것입니다. 프로그래머 가 참조 유형 에 추가 하는 것을 허용하지 마십시오 .constmutable

그대로, 자격 없이는 불변입니다.

그리고 const정규화 되지 않았기 때문에 참조 유형에서 true를 산출하는 것이 혼란 스러울 것입니다 is_const.

특히 참조를 변경하는 구문이 존재하지 않는다는 단순한 사실에 의해 불변성이 강제되기 때문에 이것이 합리적인 타협이라고 생각합니다.


답변

이것은 C ++의 특이한 / 기능입니다. 참조를 유형으로 생각하지 않지만 실제로는 유형 시스템에 “앉아”있습니다. 이것은 어색해 보이지만 (참조가 사용될 때 참조 시맨틱이 자동으로 발생하고 참조가 “비정상적”이라는 점을 감안할 때) 참조가 외부의 별도 속성이 아닌 유형 시스템에서 모델링되는 몇 가지 확실한 이유가 있습니다. 유형.

첫째, 선언 된 이름의 모든 속성이 유형 시스템에 있어야하는 것은 아니라는 점을 고려해 보겠습니다. C 언어에서는 “스토리지 클래스”와 “연결”이 있습니다. 이름은로 도입 될 수 있습니다 extern const int ri. extern여기서은 정적 스토리지 클래스 및 링크의 존재를 나타냅니다. 유형은 단지 const int입니다.

C ++는 표현식이 유형 시스템 외부에있는 속성을 가지고 있다는 개념을 분명히 수용합니다. 이제 언어에는 표현식이 표시 할 수있는 비 유형 속성의 증가하는 수를 구성하려는 시도 인 “값 클래스”개념이 있습니다.

그러나 참조는 유형입니다. 왜?

C ++ 튜토리얼에서는 type을 갖는 것으로 const int &ri소개 된 ri것과 같은 선언이 const int의미론을 참조 한다고 설명했습니다 . 그 참조 의미론은 유형이 아닙니다. 이름과 저장 위치 사이의 비정상적인 관계를 나타내는 일종의 속성 일뿐입니다. 또한 참조가 유형이 아니라는 사실은 유형 생성 구문이 허용하더라도 참조를 기반으로 유형을 생성 할 수없는 이유를 합리화하는 데 사용되었습니다. 예를 들어, 참조에 대한 배열 또는 포인터는 불가능합니다 : const int &ari[5]const int &*pri.

그러나 실제로 참조 유형이므로 decltype(ri)규정되지 않은 일부 참조 유형 노드를 검색합니다. 를 사용하여 기본 유형에 도달하려면 유형 트리에서이 노드를지나 내려 가야합니다 remove_reference.

를 사용 ri하면 참조가 투명하게 확인되므로 ri“모양 및 느낌 i“이되며 “별칭”이라고 할 수 있습니다. 그러나 유형 시스템에서는 ri실제로 ” reference to const int “인 유형이 있습니다.

왜 참조 유형입니까?

참조가 유형 이 아니라면 이러한 함수는 동일한 유형을 갖는 것으로 간주됩니다.

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

그것은 거의 자명 한 이유 때문일 수 없습니다. 유형이 같으면 선언이 어느 정의 에나 적합하므로(int) 함수가 참조를받는 것으로 의심되어야합니다.

마찬가지로 참조가 유형이 아닌 경우 다음 두 클래스 선언은 동일합니다.

class foo {
  int m;
};

class foo {
  int &m;
};

한 번역 단위가 하나의 선언을 사용하고 동일한 프로그램의 다른 번역 단위가 다른 선언을 사용하는 것이 옳습니다.

사실 참조는 구현의 차이를 의미하며 C ++의 유형은 엔티티의 구현과 관련이 있기 때문에이를 유형과 분리하는 것은 불가능합니다. 말하자면 비트 단위의 “레이아웃”입니다. 두 함수의 유형이 동일한 경우 동일한 이진 호출 규칙으로 호출 할 수 있습니다. ABI는 동일합니다. 두 구조체 또는 클래스의 유형이 동일한 경우 해당 레이아웃은 모든 멤버에 대한 액세스 의미뿐만 아니라 동일합니다. 참조의 존재는 유형의 이러한 측면을 변경하므로이를 유형 시스템에 통합하는 것은 간단한 디자인 결정입니다. (그러나 여기에서 반론에 유의하십시오. 구조체 / 클래스 멤버는 일 수 있으며 static표현도 변경되지만 유형이 아닙니다!)

따라서 참조는 유형 시스템에서 “두 번째 클래스 시민”으로 존재합니다 (ISO C의 함수 및 배열과 다르지 않음). 참조에 대한 포인터 또는 배열의 선언과 같이 참조로 “할 수없는”특정 작업이 있습니다. 그러나 그것이 그들이 유형이 아니라는 것을 의미하지는 않습니다. 말이되는 방식으로 유형이 아닙니다.

이러한 모든 2 등급 제한이 필수적인 것은 아닙니다. 참조 구조가 있다는 점을 감안할 때 참조 배열이있을 수 있습니다! 예

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

그러나 이것은 그것이하는 것처럼 보이는 것을하지 않습니다. 이 선언에서 자격 aconst부여 되는 것은 요소가 아니라 요소입니다! 즉, 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어레이 간 포인터 전환을 억제하고, 동작에 어레이 형 자체 의 크기를 계산.


답변

const X & x”는 x가 X 개체의 별칭을 의미하지만 x를 통해 해당 X 개체를 변경할 수 없습니다.

그리고 봐라 std :: is_const를.


답변