C++ 공부 - 모두의 코드 (5)

자훈·2023년 11월 29일
1

C++ / C study

목록 보기
3/8
post-thumbnail

미니 정리

static에 대한 개념
1. 정적변수: 프로그램이 로드 될 때 한 번만 초기화
2. 정적함수: 파일범위에 사용되는 함수. 특정 파일에 속한다.
3. 정적 클래스 멤버: 해당 클래스의 모든 객체에 하나된 공유변수이다. 설정한 값은 어떤 객체 인스턴스로 호출하든 똑같다.
SharedValue obj1, obj2;
obj1.sharedvar = 5 std:: cout << obj2.sharedvar // 5로나옴
4. 정적 메소드: 마찬가지로 특정 클래스 인스턴스에 종속되지 않는 메소드이다. 메소드와 함수가 비슷한 측면이 있어보이지만, 메소드는 클래스의 멤버함수를 말한다.
Myclass::myfunc() 형식으로 클래스 이름으로 직접호출이 가능하다.

static const에서 const는 변수가 수정될 수 없음을 static은 구조체/클래스 내에서 공유됨으로 할당된다는 것을 말한다. 즉 상수값이 공유되길 원할 때 사용하는 형식.

📌 Template Meta Programming

여태까지의 타입은 어떠한 객체에 어떤 타입을 저장하는지 나타내기 위해서 사용되어왔지만, 타입 자체가 값을 가지지는 않았다. 그러나 템플릿을 사용하게 되면 타입 자체를 가지고 연산을 하고, 타입에 어떠한 값을 부여할 수 있다는 사실이다.

또한 타입은 컴파일 시 확정되기 때문에, 연산도 마찬가지로 컴파일 단계에서 끝나게 된다. 컴파일 타임에 생성되는 코드로 프로그래밍 하는것이 Template meta programming 줄여서 TMP이다.

#include <iostream>
template <int N>
struct Factorial {
  static const int result = N * Factorial<N - 1>::result;
};

template <>
struct Factorial<1> {
  static const int result = 1;
}; //템플릿 특수화. 재귀적인 팩토리얼 구조를 끝내는 장치. 

int main() {
  std::cout << "6! = 1*2*3*4*5*6 = " << Factorial<6>::result << std::endl;
}

위 코드에서 실질적으로 값을 가지고 있는 객체는 없지만 컴파일 하게 되면 값은 나온다. 우리가 보고 있는 것은 Factorial<6>이라는 타입이 만들어 낸 것만 보고 있다.

TMP는 컴파일시에 코드가 진행되기에 버그를 찾는 것이 힘들고, 디버깅이 불가능한 불편한 점들이 있기 때문에 이용하는 경우는 제한적이지만 속도가 매우 중요한 프로그램의 경우 TMP를 사용하여 런타임 속도를 향상시킬 수 있다.

어느 정도 이해가 되었다면 아래의 예제코드를 템플릿을 활용하여 바꿔보시길!

int gcd(int a, int b) {
  if (b == 0) {
    return a;
  }

  return gcd(b, a % b);
}

🔍 또 다른 TMP 예시코드

#include <iostream>

template <int X, int Y>
struct GCD {
  static const int value = GCD<Y, X % Y>::value;
};

template <int X>
struct GCD<X, 0> {
  static const int value = X;
};

template <int N, int D = 1>
struct Ratio {
 private:
  const static int _gcd = GCD<N, D>::value;

 public:
  typedef Ratio<N / _gcd, D / _gcd> type;
  static const int num = N / _gcd;
  static const int den = D / _gcd;
};

template<class R1, class R2>
struct Calc_ratio
{
        using add = Ratio<R1::num * R2::den + R1::den * R2::num, R1::den * R2::den>;
        using substract = Ratio<R1::num * R2::den - R1::den * R2::num, R1::den * R2::den>;
        using multi = Ratio<R1::num * R2::num, R1::den * R2::den>;
        using divide = Ratio<R1::num * R2::den, R1::den * R2::num>;
};

int main() {
  using r1 = Ratio<2, 3>;
  using r2 = Ratio<3, 2>;
  using calc = Calc_ratio<r1, r2>;

  using r3 = calc::add;
  std::cout << "2/3 + 3/2 = " << r3::num << " / " << r3::den << std::endl;

  sing cal = Calc_ratio<r1, r3>;
  using r4 = cal::multi;
  std::cout << "13 / 6 * 2 /3 = " << r4::num << " / " << r4::den << std::endl;

using을 활용하여 타입의 별칭을 보다 직관적으로 나타내었고, Cal_ratio안에 기능들을 묶어 놓았다. r1과 r2라는 별칭에 유리수 값들을 저장하고, 계산기에 그 값들을 전달한다. 전달한 값들은 calc안에 있으므로, 내가 호출하는 멤버함수에 따라 그 값들이 컴파일 시 계산되어 호출된다. 아까 말했듯, 어떠한 객체도 여기서는 값을 가지고 있지 않고, 컴파일시에 사칙연산이 작동하여 값을 출력하는 것이다.

템플릿 특수화 시에는 단순한 식별자만 입력해주어야 한다.

🔒 의존 타입

의존 타입(dependent type)은 어떤 타입이 다른 타입에 종속(dependent)되어 있는 상황을 의미합니다. 주로 템플릿 메타프로그래밍에서 나타납니다.

#include <iostream>

template <typename T, T Base>
struct Factorial {
    static const T value = Base * Factorial<T, Base - 1>::value;
};

template <typename T>
struct Factorial<T, 0> {
    static const T value = 1;
};

int main() {
    const int result = Factorial<int, 5>::value;
    std::cout << "5! = " << result << std::endl;

    const int result_double = Factorial<int, 3>::value;
    std::cout << "3! = " << result_double << std::endl;

    return 0;
}

Factorial은 컴파일시 팩토리얼 값을 계산하여 출력한다. 동작과정을 코드적으로 나타낸다면

Exponential<int, 5>::value
= 5 * Exponential<int, 4>::value
= 5 * 4 * Exponential<int, 3>::value
= 5 * 4 * 3 * Exponential<int, 2>::value
= 5 * 4 * 3 * 2 * Exponential<int, 1>::value
= 5 * 4 * 3 * 2 * 1 * Exponential<int, 0>::value
= 5 * 4 * 3 * 2 * 1 * 1
= 120

해당 코드처럼 동작하여 5부터 1까지 계속 곱하게 된다. 그리고 템플릿 특수화가 0일 때 사용되어져 있기 때문에, value를 1을 곱해주고 재귀적인 메타프로그래밍이 종료하게 된다.
Factorial 템플릿은 인스턴스화 되기 전부터, Base의 값에 의존하고 있다.

또 다른 예시 코드.

#include <iostream>

template <typename T, T Value>
struct Square {
    static const T result = Value * Value;
};

int main() {
    const int value = 5;

    // 의존 타입 사용 예시
    const int squared_value = Square<int, value>::result;

    std::cout << "Square of " << value << " is: " << squared_value << std::endl;

    return 0;
}

Square 템플릿은 템플릿의 인스턴스화가 되기 전에 value의 값에 의존하고 있습니다. 이것이 의존타입에 대한 예시입니다.

본 내용은 씹어먹는c++을 통해 알게된 내용을 개인적인 공부를 위해 정리한 포스팅입니다. 저작권을 해칠 의도가 없으며, 모든 것은 해당 블로그 저자의 지식재산입니다.
https://modoocode.com/210

0개의 댓글