[[[원리 분석]]]
반환값에 주목해보자. ptrdiff_t이다.
ptrdiff_t는 c, c++에서 사용되는 데이터 타입이다.
두개의 배열 혹은 포인터 사이의 거리(차이)를 나타내는데 사용된다.
부호 있는 정수이다.
long long이다.
[[[관찰]]]
정말 ptrdiff_t를 return할까?
first와 last의 주소를 바꿔보자
관찰 결과: last - first라는 것을 확인할 수 있다.
참고: v.end는 5가있는 위치가 아니라 그 다음 위치이다.
vector 말고 list의 결과도 확인해 보자
first = begin, last = end일 때는 동일하다.
first = end , last = begin 일 때는 다르다.
결론: vector일 때, list일 때 동작 방식이 다르다. 어떻게 다른거지?
==list일 때 함수 구==
1. 두 포인터 사이의 거리를 구하는 방법은 직접 한칸씩 이동하며 숫자를 세는 방법이다.
2. 리스트의 반복자 클래스는 +연산자 오버로딩에서 포인터의 다음 주소가 return되게 구현되어있다.
3. list같은 경우는 end의 다음 주소는 처음 주소인가보다. 그렇기 떄문에 결과값으로 1이 나온거 같다.
4. 벡터는 다른 방식으로 구현해야 한다.
end에서 계속 1을 더하면 끝없이 앞으로 전진하기 때문에 begin과 만날일이 없기 때문이다.
확인해보자.
실행이 끝나지 않는 것을 확인할 수 있다.
==vector일 때 함수 구현==
앞의 관찰에서 distance함수에서 vector, list가 다르게 동작함을 알 수 있었다.
vector, list는 무슨 차이가 있을까?
둘을 구분지을 수 있는 무언가가 존재해야 동작도 다르게 시킬 수 있을 것이다.
[[중요]]
1. 벡터는 random_access_iterator_tag이다.
2. random_access할 수 있기 때문에 인덱스끼리 - 연산이 가능하다.
"포인터로 연결되어있는 list를 -연산으로 계산할 수 없는게 당연한 것이었다"
random_access_iterator_tag와 그 외의 타입으로 나눈다.
그럼 구현해 보자.
반복자의 종류를 알아내는 방법이다.
반복자는 특성을 갖고 있는데 특성운 여러가지가 있다.
그 중 iterator_category는 다섯 가지의 종류 중 어떤 종류인지 물어보는 것이다.
vector는 random_access이다.
[[실제 비교 방법(concept 나오기 이전의 비교 방법)]]
is_same<...>::value 사용
1. 반복자 타입 T에서 반복자 카테고리를 추출한다.
2. 반복자 카테고리는 해당 반복자가 어떤 종류의 반복자인지 알려준다.
- 입력 반복자, 출력 반복자, 전방 반복자, 양방향 반복자, 랜덤 접근 반복자
3. is same<...>는 두 타입이 동일한지 여부를 검사하는 type trait다.
타입 트레이트는 템플릿 메타 프로그래밍에서 타입의 특성을 조사하거나 변환하는데 사용된다.
value는 두 타입이 동일하면 true, 다르면 false를 리턴한다. value는 함수가 아니라 변수다.
T의 반복자 카테고리가 random_access_iterator_tag와 동일한지 여부를 판단한다.
4. iterator_trait도 type trait일까 궁금할 수 있는데 아니다. 이건 표준 라이브러리에 정의된 클래스 템플릿으로 반복자의 값타입, 차분 타입, 참조 타입, 포인터 타입, 반복자 카테고리 등을 추출한다.
is_same<T, T>라고 한 것은 부분 특수화이다.
템플릿 매개변수가 두개일 때 두 매개변수가 동일한 타입인 경우를 처리하기 위함이다.
==typename을 붙이는 이유==
typename은 "템플릿 매개 변수에 종속된 타입"이라면 붙인다.
std::iterator_traits<T>::iterator_category에서 T가 템플릿 매개 변수다. T는 앞에서 template<class T>로 가져온 템플릿 매개 변수다.
컴파일러는 T가 어떤 타입인지 명확하게 알지 못하기 때문에 iterator_category가 타입인지 다른것인지 알지 못한다.
따라서 typename std::iterator_traits<T>::iterator_category 라고 해야 iterator_category가 타입임을 명시적으로 지정한다.
그러니까...
컴파일러가 템플릿을 파싱할 때 종속 이름이 타입인지 값인지 모호할 수 있다.
예를 들어 T::A가 템플릿 안에서 사용될 때 T가 템플릿 매개변수라면 B가 타입인지 변수인지 함수인지 값인지 어떤 엔티티인지 알 수 없다.
typename을 붙이면 이 이름은 타입이다! 라고 명시적으로 알려주는 것이다.
정리: 템플릿 멤버 변수 T가 있을 때 컴파일러는 T가 어떤 타입인지 명확하게 알지 못한다.
따라서 iterator_category같이 탬플릿 매개 변수에 종속된 타입이라면 이것이 타입임을 명확히 알려주기 위해 typename을 붙여야 한다.
그렇게 하지 않으면 이게 타입을 나타내는지 값을 나타내는지 변수인지 함수인지 컴파일러는 알 수 없다.
따라서 에러가 난다.
템플릿: 템플릿이 정의되는 시점에는 T가 어떤 타입인지 아직 결정되지 않았다.
T는 템플릿이 인스턴스화 될 때(객체가 될 때) 구체적인 타입으로 대체된다.
따라서 컴파일러가 템플릿 정의 시점에서 T가 무슨 타입인지 모르고 T에 종속된 멤버들도
무슨 타입인지 알 수 없다. T가 결정되지 않았기 때문이다.
==다시 본론으로 돌아와서==
is_same<...>::value == is_same_v<>
같은 방식이다.
list와 vector에 대해 distance함수를 동시에 실행해 보고자 한다.
컴파일 시점에서 if와 if constexpr이 어떻게 동작하는지를 알아보자
[if로 동작하는 경우]
vector를 가지고 my_distance 함수를 호출하면 벡터의 begin과 end가 템플릿 매개변수 T로 넘어간다
iterator_traits<T>::iterator_category와 random_access_iterator_tag 가 동일하므로
if는 true가 나오고 return e - b가 실행 된다.
random_access타입의 함수는 -로 거리 차이를 알 수 있도록 지원해 주기 때문에
컴파일 시점에서 아무 문제 없다고 판단하고 성공시킨다.
list를 가지고 위와 같이 했을 때 if는 false가 나오기 때문에 else문이 실행될 것 같다.
else문으로 넘어가면 되니까 컴파일 시점에서도 에러가 없을 것 같지만 에러가 있다.
if문을 사용했을 경우 조건이 false라고 해도 return e - b 이 부분에 대한 유효성 검사를 한다.
그렇기 때문에 T가 - 연산을 지원하지 않는다고 나오는 것이다.
false임에도 불구하고!!! 컴파일 시점에서 return e - b를 유효성 검사를 한다!!!
런타임이 아니라 컴파일 타임이니까 코드를 끝까지 읽어보는 것이다.
[if constexpr]
컴파일 타임에 값을 결정한다.
이렇게 사용하면 조건이 false일 때 유효성 검사를 하지 않는다.
따라서 코드를 모두 검사하지 않고 true일 때만 검사한다.
true일 때만 코드로 만들어내고 아니면 코드에서 제외한다.
선택적 컴파일이라고 한다.
다른 해결 방법으로는 우회로를 만드는 것인데 디스패칭이라고 한다.
학습 장소: 한국공학대학교 게임공학과 수업
'---C++ 역량 강화--- > STL 공부' 카테고리의 다른 글
[c++20] concept (0) | 2024.06.06 |
---|---|
템플릿 메타프로그래밍 (0) | 2024.06.06 |
copy (0) | 2024.06.04 |
stable_sort (0) | 2024.06.04 |
uniform initialization (0) | 2024.06.03 |