스토리텔링 개발자

[Effective C++] 41. 템플릿 프로그래밍 본문

개발/Effective C++

[Effective C++] 41. 템플릿 프로그래밍

김디트 2024. 7. 11. 11:09
728x90

항목 41. 템플릿 프로그래밍의 천릿길도 암시적 인터페이스와 컴파일 타임 다형성부터

 

 

 

객체 지향 프로그래밍의 축
  1. 명시적 인터페이스(explicit interface)
    • 명시적으로 적시된 인터페이스를 통해 객체를 활용할 수 있다.
  2. 런타임 다형성(runtime polymorphism)
    • 런타임에 객체의 타입을 유동적으로 체크한다.
class Widget
{
public:
    Widget();
    virtual ~Widget();
    virtual std::size_t size() const;
    virtual void normalize();
    void swap(Widget& other);
    ...
};

void doProcessing(Widget& w)
{
    if(w.size() > 10 && w != someNastyWidget)
    {
        // 헤더 등에서 Widget 인터페이스를 지원해야 한다.(명시적 인터페이스)
        Widget temp(w);
        // virtual 함수를 호출한다.(런타임 다형성)
        temp.normalize();
        temp.swap(w);
    }
}

 

 

 

템플릿과 일반화 프로그래밍의 축
  1. 암시적 인터페이스(implicit interface)
    • 인터페이스가 명확히 적시되어 있지 않아도 특정 인터페이스가 있음을 가정할 수 있다.
  2. 컴파일 타임 다형성(compile-tiem polymorphism)
    • 컴파일 타임에 타입 형식을 결정한다.
template<typename T>
void doProcessing(T& w)
{
    // w가 수반되는 함수 호출이 일어날 때,
    // 해당 호출을 성공시키기 위해 템플릿의 인스턴스화(w의 인스턴스화)가 일어난다.
    // 이 인스턴스화는 컴파일 도중에 발생한다.
    // 그리고 어떤 템플릿 매개변수냐에 따라 호출 함수가 달라진다.(컴파일 타임 다형성)
    if(w.size() > 10 && w != someNastyWidget)
    {
        // w의 인터페이스는 w에 대해 실행되는 연산이 결정한다.
        // 이 템플릿이 컴파일되려면 몇 개의 표현식이 유효해야 한다.(암시적 인터페이스)
        // 이 경우 복사 생성자, normalize, swap를 지원하는 인터페이스여야 한다.
        T temp(w);
        temp.normalize();
        temp.swap(w);
    }
}

 

 

 

런타임 다형성, 컴파일 타임 다형성
  • 런타임 다형성
    • 가상 함수 호출의 동적 바인딩과 흡사하다.
  • 컴파일 타임 다형성
    • 오버로드된 함수 중 지금 호출할 것을 골라내는 과정과 흡사하다.

 

 

 

명시적 인터페이스, 암시적 인터페이스
  • 명시적 인터페이스
    • 함수 시그니처 기반으로 이루어진다.
    • 생성자, 소멸자, 함수, 매개변수 타입, 반환 타입, 각 함수의 상수성 여부.
    • typedef 타입이 있을 경우 이것 역시 포함될 수 있다.
    • 데이터 멤버의 경우 포함되지 않는다.(public으로 공개되어 있다고 하더라도)
  • 암시적 인터페이스
    • 유효 표현식(expression) 기반으로 이루어진다.
    • template<typename T> 
      void doProcessing(T& w) 
      { 
          // 암시적 인터페이스 제약
          // 1. 정수 계열 값을 반환하는 이름이 size인 함수가 있어야 한다.
          // 2. T 타입 객체 둘을 비교하는 operator!= 함수를 지원해야 한다.
          // (someNastyWidget이 T 객체라는 가정 하에)
          if(w.size() > 10 && w != someNastyWidget)
          {
              T temp(w);
              temp.normalize();
              temp.swap(w);
          }
      }
    • 실제로는 T는 위 주석의 두 가지 제약 중 어떤 것도 만족시킬 필요가 없다.
      • size 함수의 경우
        • T가 size 멤버 함수를 지원해야 한다. 하지만 수치 타입을 반환할 필요는 없다.
        • 즉, size 멤버 함수가 반환하는 어떤 x 타입 객체와 int가 함께 호출될 수 있는 operator>가 성립된다면, 어떤 값을 반환해도 된다.
        • 심지어 operator> 함수가 x 타입을 받아들일 필요도 없다. X 타입이 operator> 함수가 받아들이는 매개변수로 암시적 변환만 가능해도 된다.
      • operator!= 함수의 경우
        • operator!= 함수가 X 타입, Y 타입 객체를 받아들인다고 하면..
        • T타입이 X로 변환될 수 있고, someNastyWidget 타입이 Y로 변환될 수만 있다면 성공 가능하다.
    • 세세한 제약을 일일이 따질 필요 없이, 전체적인 제약만 맞으면 OK.
    • 결과적으로 doProcessing 템플릿이 타입 매개변수 T에 대해 요구하는 암시적 인터페이스란? 
      • 해당 표현식의 경우, if 문에 만족하는 boolean 표현식이기만 하면 된다.
      • 복사 생성자, normalize, swap 함수에 대한 호출이 T 타입 객체에 대해 '유효'하기만 하면 된다.

 

728x90
Comments