본문 바로가기
NC University/Advanced C++

4일차 - 지연된 인스턴스화 (lazy instantiation)

by 날쑤 2015. 7. 16.

Intro

  아래의 코드가 컴파일이 되지않음은 누구나 쉽게 알 수 있다.

  그렇다면 다음 코드는 컴파일이 될까?

  처음의 코드와 별 차이가 없어보이지만 위의 코드는 컴파일이 된다. 왜냐하면 foo는 사용되지 않은 템플릿 함수여서 컴파일러는 foo() 함수에 대한 코드를 생성하지 않기 때문이다. 이렇듯 함수/클래스 템플릿에서 사용되지 않은 부분에 대한 코드를 생성하지 않는 것을 지연된 인스턴스화라고 한다. 만약 위의 코드에서 주석을 지워서 foo()를 호출한다면, 그제서야 컴파일러는 코드를 생성하고 역참조에 대한 에러를 발생시킬 것이다.

클래스 내부의 static 변수

  C++에서는 클래스 내부에 선언되어 있는 정적 변수의 초기화는 클래스 외부에서 선언할 때 일어난다. 아래와 같은 간단한 클래스를 생각해보자.

  저 상태에서 A::print() 함수는 10을 출력할 것이다. 하지만 주석을 제거하면 B타입의 변수 bbb가 초기화되면서 aaa를 20으로 바꾼다. 그러므로 이 경우에는 A::print() 함수가 20을 출력할 것이다. 이로써 일반 클래스 내부의 정적 변수가 외부선언될 때 초기화된다는 사실을 확인했다.

클래스 템플릿 내부의 static 변수

  이번에는 위와 모양은 유사하지만 A를 클래스 템플릿으로 만들어보자. 아마도 아래와 같은 코드가 나올 것이다.

  이번에는 두 변수에 대해 모두 외부선언을 했다. 그럼 print() 함수는 20을 출력할까? 안타까게도 결과값은 10이 출력된다. 이번에는 외부선언도 했는데 왜 이런 결과가 나온 것일까? 답은 bbb가 템플릿 정적변수인데, 사용된 적이 없어서 코드 자체가 생성되지 않았기 때문이다. 즉, 여기서 다시 한번 지연된 인스턴스화가 등장한다. 결국 의도대로 20을 출력되게 하려면 print() 함수 호출에 앞서서 bbb를 명시적으로 사용하거나, 혹은 외부 선언을 다른 방식으로 수정해줘야 할 것이다. 하지만 전자는 일반적인 해법이라고 보기는 어렵다.

  일반적으로 클래스 템플릿 안에 있는 static 멤버를 외부선언할 때에는 다음과 같이 전문화 문법을 사용하는 것이 좋다. 이러면 전문화된 타입의 static 멤버는 항상 초기화되어 있음을 보장받을 수 있다.

  위의 코드를 추가하면 bbb가 제대로 초기화되므로 print() 함수는 20을 출력할 것이다. 그러나 만약에 사용될 타입이 많을 경우라면 전문화 코드를 일일이 치는 것도 번거로운 일이 될 것이다. 이 경우에는 매크로를 선언해서 쓰는 것이 좋은 방법이 될 것이다.