ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Effective C++...42
    일지 2022. 8. 18. 07:43

    매개변수에 독립적인 코드는 템플릿으로부터 분리시키자

    템플릿의 코드 비대화 문제

    템플릿을 아무 생각 없이 사용하다 보면 거의 똑같은 코드와 데이터가 여러 벌 중복되어 생성될 수 있다.

     

    이런 문제를 해결하기 위해서는 공통성 및 가변성 분석, 풀어쓰면 만들고 있는 클래스에 다른 클래스와 중복되는 부분이 있으면 해당 부분을 새로운 클래스로 만들고 합성을 통해 공통부분을 유지하는 전략을 사용할 수 있다.

     

    가령 다음과 같이 정방 행렬을 나타내는 템플릿이 존재하는데 해당 클래스에 역행렬을 만들어주는 invert 함수가 존재하는 경우 중복이 발생할 것이다.


    template<typename T, std::size_t n>

    class SquareMatrix {

    public:

        ...

        void invert();

    };

     

    SquareMatrix<double, 5> sm1; // 크기만 다른 템플릿 인스턴스

    sm1.invert(); // 크기만 다른 동일한 모양의 함수가 여러 개 생성된다.

    SquareMatrix<double, 10> sm2;

    sm2.invert();


     

    매개변수에 독립적인 코드 분리

    코드 비대화 문제를 해결하는 방법은 매개변수에 영향을 받지 않는 함수를 가지는 기본 클래스를 만들고 기본 클래스를 상속받는 파생 클래스를 만들어 중복을 제거하는 것이다.


    template<typename T>

    class SquareMatrixBase {

    protected:

        void invert(std::size_t matrixsize);

    };

    template<typename T, std::size_t n>

    class SquareMatrix : private SquareMatrixBase<T> {

    private:

        using SquareMatrixBase<T>::invert;

    public:

        ...

        void invert() { this->invert(n); }

    };


     

    이 구현에 문제가 하나 남아 있는데 기본 클래스에서 파생 클래스의 데이터 정보를 알 수 없기 때문이다.

    이는 기본 클래스에 데이터 포인터를 추가하여 해결할 수 있다.


    template<typename T>

    class SquareMatrixBase {

    protected:

        SquareMatrixBase(std::sized_t n, T *pMem)

            : size(n), pData(pMem) { }

        void invert(std::size_t matrixsize);

    private:

        std::size_t size;

        T *pData;

    };

    template<typename T, std::size_t n>

    class SquareMatrix : private SquareMatrixBase<T> {

    public:

        SquareMatrix() : SquareMatrixBase<T>(n, data) { }

        ...

    private:

        T data[n*n];

    };


     

    매개변수 독립 코드를 분리할 때의 장, 단점

    당연하게도 이러한 구현에는 장점과 단점이 공존한다.

     

    장점으로는 실행 코드의 크기가 작아지므로 프로그램 작업 세트도 작아지고 결과적으로 캐시 참조 지역성도 향상된다는 점이 있다. 쉽게 말해 속도가 빨라진다.

     

    단점으로는 생성되는 코드의 효율이 떨어질 수 있는데 크기가 고정되어 있는 버전의 경우 컴파일러 단에서 최적화가 들어갈 여지가 많아 더 좋은 코드가 생성될 가능성이 높다.

     

    번외로 효율 측면에서 고민해야 할 게 있는데 기본 클래스에 포인터를 추가하는 것은 파생 클래스 입장에서 이미 알고 있는 정보를 추가로 들고 있는 것이기 때문에 불필요하게 최소 하나의 포인터 크기만큼 크기가 커진다는 것이다.

     

    ※ 장, 단점과 효율을 따져서 템플릿을 작성해야 한다.

     

    댓글

Designed by Tistory.