Notice
Recent Posts
Recent Comments
Link
«   2025/04   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
Archives
Today
Total
관리 메뉴

게임 개발 메모장

[ C++ ] 가변 인자 템플릿 본문

언리얼 엔진/C++

[ C++ ] 가변 인자 템플릿

Dev_Moses 2023. 12. 23. 21:10

가변 인자(Variable argument)란 말 그대로 고정되지 않은 인자를 말한다. 

C언어의 printf() 함수처럼 정해지지 않은 개수의 인자를 받아야 할 때 사용한다. 

가변 인자는 아래와 같이 인자를 ... 표시로 나타낸다.

void func(int ...) { }

 

위 표현은 int 타입의 매개변수를 사용자가 입력한 만큼 받겠다는 의미이다.

따라서 함수 호출 시 아래와 같이 사용할 수 있다.

func(1, 3, 2, 5, 8);
func(1, 2);
func(4);
func(5, 1, 4, 8);
...

 

 

가변인자 템플릿(Variadic template)

  • C++11부터 지원되는 문법
  • 가변인자 템플릿(클래스 템플릿, 함수 템플릿)의 기본 모양
  • 가변인자 템플릿의 인자 "Types"는 여러개의 타입을 나타냄
  • 가변인자 함수 템플릿의 인자인 "args"안에는 여러개의 값이 들어있고 "Parameter Pack"이라고 함
#include <iostream>
using namespace std;

// 가변인자 클래스 템플릿.
template<typename ... Types> class tuplex
{

};

// 가변인자 함수 템플릿
template<typename ... Types>
void foo(Types ... args)
{

}

int main()
{
    // 가변인자 클래스 템플릿(타입 생략, 복수의 타입까지...)
    tuplex<> t0p;
    tuplex<int> t1;
    tuplex<int, char> t2;
    
    // 가변인자 함수 템플릿(인자 생략, 복수의 인자까지...)
    foo();
    foo(1);
    foo(1, 3.4, "A");
}

 


Parameter Pack

  • 가변인자 템플릿으로 전달받은 파라미터 집합을 Parameter Pack이라 함
  • sizeof...(args), sizeof...(Types) 함수를 이용해서 Parameter Pack 요소의 갯수를 얻어 올 수 있음
  • goo(args...); 받은 Parameter Pack을 다시 전달할때 요소 전체를 풀어쓰는 방법을 Pack Expansion이라 함
#include <iostream>
using namespace std;

// Pack expansion을 통해 Parameter Pack을 전달 받을 함수
void goo(int n, double d, const char* s)
{
    cout << "goo : " << n << ", " << d << ", " << s << endl;
    // 1, 3.4, "AAA"
}

// 가변인자 함수 템플릿
template<typename ... Types>
void foo(Types ... args)
{
    // args : Parameter Pack
    
    cout << sizeof...(args) << endl; // 3
    cout << sizeof...(Types) << endl; // 3

    //goo(args); // Error
    goo(args...); // Pack expansion
                  // goo( 1, 3.4, "AAA")
}

int main()
{
    //foo();
    //foo(1);
    foo(1, 3.4, "AAA"); 
        // Types : int, double, const char*
        // args : 1, 3.4, "AAA"

}

Pack Expansion

  • Parameter Pack을 사용하는 패턴에 "..." 붙이면 해당 패턴이 ',' 구분자로 확장되는 형태임
  • args이 1, 2, 3을 가지고 있을때
    • args... -> 1, 2, 3 
    • (--args)... -> 0, 1, 2
    • func(args)... -> func(1), func(2), func(3)
    • func(args...) -> func(1, 2, 3)
    • func2(func(args...)) -> func2(func(1, 2, 3))
    • func2(func(args)...)) -> func2(func(1), func(2), func(3))
  • Pack Expansion은 함수 호출 인자 또는 list 초기화를 사용한 표현식에서만 사용할 수 있음
    • 배열 이용
      • 컴파일러에 따라 빈값이 허용 안될 수 있으므로 첫번째 요소를 더미로 0으로 지정
      • 함수가 반환 타입이 void 경우 대비해서 대체 반환값 (x, 0) 패턴 사용
        • int x[] = { 0, (print(args), 0)... };
    • 초기화 리스트 이용
      • 빈값이 허용 됨으로 배열에 비해서 첫번째 요소 더미 0을 생략 할 수 있음
      • initializer_list<int> e = { (print(args), 0)... };
#include <iostream>
using namespace std;

void goo(int n, double d, const char* s)
{
    cout << "goo : " << n << " " << d << " " << s << endl;
}

int hoo(int a) { return -a; }

int print(int a)
{
    cout << a << ", ";
    return 0;
}

template<typename ... Types>
void foo(Types ... args)
{
    int x[] = { args... }; // Pack expansion
    for (auto n : x)
        cout << n << endl;

    //print(args); // Error(함수 호출 인자 or list 초기화가 아님)
    //print(args...); // Error(함수 호출 인자 or list 초기화가 아님)
    //print(args)...; // Error(함수 호출 인자 or list 초기화가 아님)

    // 배열 이용
    int ea[] = { 0, (print(args), 0)... };

    // 초기화 리스트 이용
    initializer_list<int> ei = { (print(args), 0)... };

}

int main()
{
    foo(1, 2, 3);
     // Types : int, int, int
     // args : 1, 2, 3
}

 

 

Expansion 예제

#include <iostream>
using namespace std;

template<typename ... Types> void foo(Types ... args)
{
    // Types : int, double

    int x[] = { args... };
    pair<Types...> p1; // pair<int, double> // OK
    tuple<Types...> t1; // tuple<int, double> // OK

    tuple<pair<Types...>> t2; // tuple<pair<int, double>> // OK
    tuple<pair<Types>...> t3; // tuple<pair<int>, pair<double>> // Error

    tuple<pair<int, Types>...> t4; // tuple<pair<int, int>, pair<int, double>> // OK

    pair<tuple<Types...>> p2; // pair<tuple<int, double>> // Error
    pair<tuple<Types>...> p3; // pair<tuple<int>, tuple<double>> // OK

}

int main()
{
    foo(1, 3.4); 
      // args : 1, 3.4
      // Types : int, double
}

'언리얼 엔진 > C++' 카테고리의 다른 글

[ C++ ] 가상 상속  (0) 2023.12.24
[ C++ ] 가상 함수와 가상 함수 테이블  (0) 2023.12.24
[ C++ ] 포인터와 레퍼런스의 차이  (0) 2023.12.17
[ C++ ] 깊은 복사, 얕은 복사  (0) 2023.12.09
[ C++ ] 메모리 할당  (0) 2023.12.06