개요
C 언어의 포인터과 유사한 의미를 갖는 참조를 C++에서 도입했다. 이러한 이유로 포인터 관련 용어과 참조 관련 용어가 서로 결이 다르다. 본 게시글은 포인터 관련 용어를 중심으로 설명한다.
특히, similar type과 qualification-combinded type는 포인터과 관련된다.
cv-qualifier 표기법
int const* volatile* const m1{ nullptr };
m1
타입을 다음과 같이 cv-qualifier과 pointer으로 서로 대응하는 순서 집합으로 표현할 수 있다.
- cv-qualifier 순서 집합: ({const}, {volatile}, {const})
- pointer 순서 집합: (
int
,int*
,int**
)
코딩 순서를 따라 *
를 만나면, 체크하는 *
바로 이전 cv-qualifier를 cv-qualifier 순서 집합에 추가하고, 체크하는 *
이전에 cv-qualifer를 제거한 타입 이름을 pointer 순서 집합에 추가한다. 배열 인덱서 []
를 만나거나 declarator를 만나면 곧바로 뒤에 가상의 *
가 있는 것처럼 순서 집합에 추가한다.
동일한 개념을 멤버 포인터에 대해서도 동일하게 적용할 수 있다.
따라서 pointer 순서 집합은 *
또는 []
가 순차적으로 증가하고, []
의 대응하는 cv-qualifier는 공집합이다.
기존 집합에서 마지막 요소를 제거하면 이전 집합이 가르키는 타입을 의미한다.
- cv-qualifier 순서 집합: ({const}, {volatile})
- pointer 순서 집합: (
int
,int*
)
int const* volatile*const m2[10]{ };
배열 m2
타입을 다음과 같이 cv-qualifier과 pointer으로 서로 대응하는 순서 집합으로 표현할 수 있다.
- cv-qualifier 순서 집합: ({const}, {volatile}, {const}, {})
- pointer 순서 집합: (
int
,int*
,int**
,int**[10]
)
기존 집합에서 마지막 요소를 제거하면 배열의 요소 타입을 의미한다.
- cv-qualifier 순서 집합: ({const}, {volatile}, {const})
- pointer 순서 집합: (
int
,int*
,int**
)
int const* volatile*const m3[]{ };
배열 m3
타입을 다음과 같이 cv-qualifier과 pointer으로 서로 대응하는 순서 집합으로 표현할 수 있다.
- cv-qualifier 순서 집합: ({const}, {volatile}, {const}, {})
- pointer 순서 집합: (
int
,int*
,int**
,int**[]
)
기존 집합에서 마지막 요소를 제거하면 배열의 요소 타입을 의미한다.
- cv-qualifier 순서 집합: ({const}, {volatile}, {const})
- pointer 순서 집합: (
int
,int*
,int**
)
struct A {
int volatile* v_{};
};
int volatile const* const A::* volatile m4 = &A::v_;
m4
타입을 다음과 같이 cv-qualifier과 pointer으로 서로 대응하는 순서 집합으로 표현할 수 있다.
- cv-qualifier 순서 집합: ({const, volatile}, {const}, {volatile})
- pointer 순서 집합: (
int
,int* A::
,int* A::*
)
기존 집합에서 마지막 요소를 제거하면 이전 집합이 가르키는 타입을 의미한다.
- cv-qualifier 순서 집합: ({const, volatile}, {const})
- pointer 순서 집합: (
int
,int* A::
)
cv-qualifier 순서 집합의 마지막 cv-qualifier를 해당 타입의 top-level cv-qualifier라 한다. top-level cv-qualifier를 제외한 나머지 cv-qualifier 순서 집합을 해당 타입의 cv-qualification signature라 한다.
역순으로 배치하면 두 집합을 해당 타입의 qualification-decomposition라 한다. 역순으로 배치하면 top-level cv-qualifier가 맨 앞에 위치하게 된다.
해당 게시글은 코딩 순서와 유사한 순서 집합을 표기법으로 사용하는데, C++ 제안 문서는 역순으로 배치한 qualification-decomposition 표기법을 따라 문서를 작성한다.
어떤 표기법이든 cv-qualifier과 pointer 타입을 분리하면, cv-qualifier를 적용하는 타겟 타입을 쉽게 알 수 있고, 본 게시글에서 설명하려는 두 포인터 타입간의 관계를 해석할 때, 요긴하게 사용된다.
reference 타입의 reference 심블을 제거하고, reference 타입의 두 순서 집합을 구성한다. 즉 reference 심블이 두 순서 집합 구성에 영향을 주지 않는다.
similar type(T1, T2):bool
similar type(T1, T2) 연산 결과는 bool값을 반환하고, similar type(T1, T2) 연산은 교환 법칙이 성립한다. 즉 similar type(T1, T2)과 similar type(T2, T1)의 결과는 아규먼트 순서와 관련없이 같은 bool 값을 반환한다.
pointer 순서 집합이 같은 T1과 T2는 similar type이다. pointer 순서 집합에서 대응하는 bounded array 타입과 unknown bounded array 타입은 같은 타입으로 판단한다. cv-qualifier 순서 집합의 대응하는 요소는 서로 다를 수 있다.
qualification-combinded type(T1, T2):T3
본 파트에서 T1, T2는 qualification-combinded type에 전달되는 첫번째, 두번째 아규먼트 타입이고, T3는 결과 타입이다.
qualification-combinded type 연산은 교환 법칙이 성립하지 않는다. 즉 qualification-combinded type(T1, T2) 연산 결과와 qualification-combinded type(T2, T1) 연산 결과는 일반적으로 서로 다르다.
교환 법칙을 보장하지 않는 이유는 similar type과 연관된다. similar type(T1, T3) 결과는 항상 true지만, similar type(T2, T3) 결과는 true를 보장하지 않는다.
similar type(T1, T3)이 true이고, T3가 T1보다 more cv-qualified하기 때문에, T1의 prvalue category에서 T3로 변환할 수 있다.
qualification-combinded type(T1, T2)를 계산하는 과정은 다음과 같다.
- T3의 cv-qualification signature는 T1과 T2의 cv-qualifier 순서 집합의 대응하는 집합의 합집합으로 결정한다.
- T3의 pointer 순서 집합은 대응하는 타입 중 하나가
[]
타입이면[]
타입으로, 그렇지 않다면 T1의 대응 타입으로 결정한다. - cv-qualifier 순서 집합의 합집합이 서로 다른 두 집합의 합집합이면, top-level cv-qualifier 방향으로 자신 위치 이후의 모든 cv-qualifier 순서 집합에 const 요소를 추가한다.
- T3의 pointer 순서 집합으로 결정한 타입이 서로 다른 타입에서 결정한 타입이면, top-level cv-qualifier 방향으로 자신 위치 이후의 모든 cv-qualifier 순서 집합에 const 요소를 추가한다.
- T3의 top-level cv-qualifier는 공집합이다.
similar type(T1, T2)마저 true이면 T1, T2, T3 타입 모두 similar type이고, T3는 T1 또는 T2보다 최소 more cv-qualfied하고, 따라서 T1 또는 T2의 prvalue category에서 T3로 변환할 수 있다.
같은 원리로, similar type(T1, T2)가 true이고, T3이 T2과 같으면, T1의 prvalue category에서 T2로 변환할 수 있음을 의미한다.
char* pc;
char const** pcc = &pc; // 변환 가능한가?
T1에서 T2로 변환 가능하기 위해서는, similar type(T1, T2)가 true이고, T2가 T1보다 more cv-qualified해야 한다.
T1: char**
- cv-qualifier 순서 집합: ({}, {}, {})
- pointer 순서 집합: (char, char*, char**)
T2: char const**
- cv-qualifier 순서 집합: ({const}, {}, {})
- pointer 순서 집합: (char, char*, char**)
similar type(T1, T2): bool
T1 pointer 순서 집합 == T2 pointer 순서 집합
qualification-combinded type(T1, T2): T3
- cv-qualifier 순서 집합: ({const}, {} ∪ {const}, {})
- pointer 순서 집합: (char, char*, char**)
T3: char const* const*
[결론]
T1: char**
T2: char const**
T3: char const* const*
T2 == T3: false
T1 -> T2 변환 불가능: 컴파일 오류
similar type(T1, T2)가 true이고, 삼항 연산자의 두번째, 세번째 피연산자가 T1, T2이면 결과 타입은 T3 타입이 된다. 아래 코딩의 결과 타입이 이전에 계산한 T3 타입과 일치함을 눈여겨 보자.
#include <print>
#include <boost/type_index.hpp>
int main() {
using namespace boost::typeindex;
char* pc{ nullptr };
char const** pcc{ nullptr };
auto pp = true ? &pc : pcc;
std::print("{}", type_id_with_cvr<decltype(pp)>().pretty_name());
return 0;
}
[출력 결과]
char const* const*
'프로그래밍 언어 > C++' 카테고리의 다른 글
standard conversion sequence (0) | 2024.02.19 |
---|---|
reference type, binding, related type, compatible type (0) | 2024.02.14 |
built-in operator를 통해 살펴보는 value category (1) | 2024.02.07 |
value category의 서로 다른 유형간 변환 (0) | 2024.02.05 |
function과 value category 이해하기 (0) | 2024.02.02 |