4. 우리는 SkinnedMesh의 구조를 코드를 통해 바로 알 수 있고 이 구조를 따라야만 한다.
이러한 형태를 명시적 인터페이스라고 한다.
가상 함수가 존재한다. 이 가상함수에 대한 호출은 런타임 다형성에 의해 이루어 진다.
5. 반대로
아래의 템플릿이 제대로 컴파일 되기 위해서는 Type T는 SetHP(), State() 등이 지원되어야 하는데
obj가 수반되는(operator==등등) 함수가 호출 될 때는 컴파일 도중 템플릿의 인스턴스화가 일어난다.
obj에 어떤 매개변수가 들어가느냐에 따라 호출되는 함수가 달라질 수 있다.
이러한 성질을 컴파일 타임 다형성이라 한다.
이들을 T가 지원해야 하는 암시적 인터페이스라 한다.
6. 말이 어렵다 하지만 풀어보면 별거 없다
• 우리가 클래스 인스턴스 만들어 사용하듯 템플릿도 매개변수에
맞는 인스턴스를 만들어야 한다. 이를 템플릿의 인스턴스화라 한
다.
• 이 과정은 매개변수마다 다르니 컴파일 과정에서 확인된다. 따라
서 컴파일 타임 다형성이라 부른다.
• 암시적 인터페이스를 표현식이라 한다.
9. 마치 C++에서 struct와 class 차이 같다.
왜 나눠났을까?
Struct/Class말고
Class/Typename
10. 두 가지 이름
iter는 C의 타입에 따라 달라진다. 이를 의존이름이라 한다.
Value는 C의 타입과는 전혀 상관없다. 이를 비의존 이름이라 한다.
위 코드는 문제가 많다.
우연히 T::const_iterator가 같은 이름을 가진 정적 변수라면?
모든 경우의 수에 대해 안전해야 한다.
T::const_iterator는 중첩가능성이 있다. 이를 중첩의존이름이라 한다.
16. 로그는 습관이니 로그를 남기도록 합시다.
MsgSender<client>를 상속받았음에도 불구하고 sendClear를 컴파일러는 알지 못한다.
컴파일러는 client가 정확하게 무엇인지 모르는 상황에서 어디서 파생된 것인지 알지를 못한다!
17. 더 파고들면
만약 서버가 메시지를 전송할 때는 꼭 암호화해서 보낸다고 하자
ServerA에만 필요한 특수한 형태의 MsgSender가 필요하다.
완전 템플릿 특수화
오로지 ServerA타입에 대해서 특수한 형태로 사용하도록 하고 있다.
만약 템플릿LogMsgSender에 ServerA가 들어가게 된다면 MsgSender는 SendClear가 없는 상황이다!
18. 방법은 많습니다.
sendClear가 상속될 때 만을 가정한다.
만약 ServerA같은 타입이 client에 들어갈 경우 컴파일 에러가 난다.
This를 사용
Using을 사용
상속받음을 명시
20. inline과 마찬가지로
• Template도 막 쓰면 오히려 코드비대화 현상이 일어난다.
• 공통성 및 가변성 분석을 하자.
• 뭐.. 이름만 어렵지 우리가 늘 해오던 짓이다.
• 다만… 템플릿은 암시적이다.
• 일반적으로 함수로 묶은 코드중복 방지는 명시적이지만
• 템플릿은 코드중복을 어떻게 해소하는지 암시적이다
22. 일단, 해결방안
상속받은 클래스의 invert가 가려지는 것을 막기 위해 using사용
오로지 사본을 피할 목적이기 때문에 protected를 사용하고 있다.
단순히 구현을 돕기 위한 용도이기 때문에 private을 이용
This는 이미 위에서 using이 사용되었으니 불필요하다
매개변수로 조건을 넣어준다
아직 멀었다! 계산을 위한 행렬 데이터가 SquareMatrix에 있을
경우 SquareMatrixBase의 invert 계산은 어떻게 하냐?
26. 이렇게 하고 싶은데..
하지만 컴파일러는 용서하지 않는다.
템플릿에 의해 만들어진 인스턴스 사이는 아무 관계가 없다.
27. 그러면 만들자
멤버 함수 템플릿
모든 T와 U타입에 대하여 SmartPtr<T>객체가 SmartPtr<U>로부터 생성될 수 있다.
일반 복사 생성자라고 한다.
하지만… 우리가 원하는 영역보다 더 많은 영역을 만들어낼 수 있다.
예를 들면 자식에서 부모, 자식에서 부모까지도 가능하도록 하고 있다.
28. 그냥은 안 된다
스마트포인터는 복사할 때 사본을 반환한다. 이점을 이용하여 여기서도 사본을 반환하고 있다.
결국 포인터의 암시적 변환을 이용하고 있다.
29. 멤버 함수 템플릿의 활용
같은 shared_ptr에서의 암시적 변환은 허용되지만 다른 타입으로 부터의 변환은 허용하지 않고 있다.
여기만 explicit이 없다
마지막으로, 일반화 복사 생성자는 컴파일러가 만드는 복사 생성자를 막는 요소가 아니다.
32. 문제가 없는 것 같지만 아래코드를 컴파일 하면 에러가 난다.
지금의 경우 operator*함수를 호출 할 때 어떤 함수를 호출하려는지 알 수 없다.
operator*의 템플릿 T가 무엇인지 모르기 때문이다.
oneHalf의 T가 int형임은 쉽게 알 수 있다. 하지만 2의 경우는 어떻게 처리할 것인가?
컴파일러는 템플릿 인자 추론과정에서 암시적 변환을 고려하지 않는다.
컴파일러는 템플릿 인자 추론을 통해 인자를 찾는다.
33. 인자추론의 수고를 덜자
템플릿 인자추론은 클래스 템플릿에서는 일어나지 않는다.
T의 정보는 해당 클래스가 인스턴스화 되는 순간 알 수 있다.
문제는 컴파일은 되는데 링크가 안 된다….
외부 함수인 operator*함수를 선언만 했지 정의가 안되어 있기 때문이다.
비멤버 함수를 선언하는 방법이 friend밖에 없다.
37. advance
반복자를 지정된 거리만큼 이동시키고 있다.
Iter += d같은 연산이 불가능하기 때문에 필요하다.
임의 접근 반복자만 위 같은 연산이 가능하다.
38. 반복자의 종류
• 입력 반복자(읽기 전용 파일포인터 연상, 한번만 읽을 수 있음)
• 출력 반복자(위에서 오로지 쓰기만 가능하도록 바뀜)
• 순방향 반복자(여러 번 읽고 쓰기 가능)
• 양방향 반복자(순방향에서 양방향만 추가)
• 임의 접근 반복자(반복자 산술연산 추가)
49. 템플릿 메타프로그래밍
• 컴파일 도중에 실행되는 템플릿 기반의 프로그램 작성
• 즉 컴파일러가 실행시키는 프로그램….
• 실행이 완료된 산출물은 다시 C++코드로 들어가 컴파일 된다.
• 런타임 영역의 연산을 컴파일시간으로 합쳐버릴 수 있다.
• 까다롭거나 불가능한 일도 해낸다.
50. 또 다시 돌아가서
아까 문제가 되었던 advance 코드
컴파일을 해보면 에러가 난
다.바로 += 부분인데 이 부분은 if문에 의해 정해진 조건일 때만 실행될 것이다. 그런데 왜 나는 것일까?
If문은 런타임 도중에 실행된다. Template은 컴파일 도중 실행된다.
컴파일 도중에 iter를 체크하기 때문에 +=의 오류를 컴파일러는 잡을 수 밖에 없다.
51. Let‟s TMP
• 루프가 없다… 헐..
• 루프효과를 내기 위해서 재귀함수를 이용한다.
• 이 재귀함수조차도 우리가 아는 형태가 아니다.
• 재귀식 탬플릿 인스턴스화를 이용한다.
• 다행이 TMP용 라이브러리들도 꽤 있다.
55. new의 뒷처리 담당
new처리자라고 하며 메모리할당 도중 문제발생시 우선적으로 호출하도록 하고 있다.
사용은 이렇게 한다.
위 코드를 실행시키니 아무 일도 안 일어난 점이 함정
new는 할당 받을 충분한 공간이 없으면 내부적
으로 순환하면서 계속 공간을 찾는다.
이때 처리자도 계속 반복 호출된다.
56. 처리자 함수의 형태
• 사용할 수 있는 메모리를 더 확보
• 다른 new 처리자를 설치
• new 처리자 설치를 제거(null을 집어넣는다.)
• 예외를 던짐
• 복귀하지 않음(꺼버림)
61. 왜 new연산자를 바꾸고 싶을까
• 잘못된 힙 사용을 탐지하기 위해
• 효율을 향상시키기 위해
• 동적 할당 메모리의 실제 사용에 관한 통계 정보를 수집하기 위해
• 할당 및 해제 속력을 높이기 위해
• 기본 메모리 관리자의 공간 오버헤드를 줄이기 위해
• 적당히 타협한 기본 할당자의 바이트 정렬 동작을 보장하기 위해
• 임의의 관계를 맺고 있는 객체들을 한군데로 나란히 모아놓기 위해
• 그때그때 원하는 동작을 수행하기 위해