Ellipsis 및 Variadic 템플릿

이 문서에서는 C++ variadic 템플릿에서 줄임표(...)를 사용하는 방법을 보여 줍니다. 줄임표는 C 및 C++에서 많은 용도를 사용했습니다. 여기에는 함수에 대한 변수 인수 목록이 포함됩니다. printf() C 런타임 라이브러리의 함수는 가장 잘 알려진 예제 중 하나입니다.

variadic 템플릿은 임의의 수의 인수를 지원하는 클래스 또는 함수 템플릿입니다. 이 메커니즘은 C++ 라이브러리 개발자에게 특히 유용합니다. 클래스 템플릿과 함수 템플릿 모두에 적용할 수 있으므로 다양한 형식 안전 및 사소한 기능과 유연성을 제공할 수 있습니다.

구문

줄임표는 variadic 템플릿에서 두 가지 방법으로 사용됩니다. 매개 변수 이름의 왼쪽에는 매개 변수 팩표시되고 매개 변수 이름의 오른쪽에는 매개 변수 팩이 별도의 이름으로 확장됩니다.

다음은 variadic 클래스 템플릿 정의 구문의 기본 예제입니다.

template<typename... Arguments> class classname;

매개 변수 팩과 확장 모두에 대해 다음 예제와 같이 기본 설정에 따라 줄임표 주위에 공백을 추가할 수 있습니다.

template<typename ...Arguments> class classname;

또는 다음 예제를 수행합니다.

template<typename ... Arguments> class classname;

이 문서에서는 첫 번째 예제에 표시된 규칙(줄임표가 연결됨)을 typename사용합니다.

앞의 예제 Arguments 에서는 매개 변수 팩입니다. 클래스 classname 는 다음 예제와 같이 가변 개수의 인수를 수락할 수 있습니다.

template<typename... Arguments> class vtclass;

vtclass< > vtinstance1;
vtclass<int> vtinstance2;
vtclass<float, bool> vtinstance3;
vtclass<long, std::vector<int>, std::string> vtinstance4;

variadic 클래스 템플릿 정의를 사용하면 하나 이상의 매개 변수가 필요할 수도 있습니다.

template <typename First, typename... Rest> class classname;

다음은 variadic 함수 템플릿 구문의 기본 예제입니다.

template <typename... Arguments> returntype functionname(Arguments... args);

Arguments 그런 다음 다음 섹션에 표시된 대로 매개 변수 팩을 확장하여 사용할 수 있습니다.

다음 예제를 포함하지만 이에 국한되지 않는 다른 형태의 variadic 함수 템플릿 구문이 가능합니다.

template <typename... Arguments> returntype functionname(Arguments&... args);
template <typename... Arguments> returntype functionname(Arguments&&... args);
template <typename... Arguments> returntype functionname(Arguments*... args);

다음과 같은 const 지정자도 허용됩니다.

template <typename... Arguments> returntype functionname(const Arguments&... args);

variadic 템플릿 클래스 정의와 마찬가지로 매개 변수가 하나 이상 필요한 함수를 만들 수 있습니다.

template <typename First, typename... Rest> returntype functionname(const First& first, const Rest&... args);

Variadic 템플릿은 연산자 sizeof...() (이전 sizeof() 연산자와 관련이 없는)를 사용합니다.

template<typename... Arguments>
void tfunc(const Arguments&... args)
{
    constexpr auto numargs{ sizeof...(Arguments) };

    X xobj[numargs]; // array of some previously defined type X

    helper_func(xobj, args...);
}

줄임표 배치에 대한 자세한 내용

이전에 이 문서에서는 매개 변수 팩 및 확장을 정의하는 줄임표 배치에 대해 설명했습니다. "매개 변수 이름의 왼쪽에는 매개 변수 팩을 의미하고 매개 변수 이름의 오른쪽에는 매개 변수 팩을 별도의 이름으로 확장합니다." 기술적으로는 사실이지만 코드로 변환할 때 혼동될 수 있습니다. 고려할 사항은 다음과 같습니다.

  • template-parameter-list(template <parameter-list>) typename... 에서 템플릿 매개 변수 팩을 소개합니다.

  • 매개 변수 선언 절(func(parameter-list))에서 "최상위" 줄임표는 함수 매개 변수 팩을 도입하며 줄임표 위치 지정이 중요합니다.

    // v1 is NOT a function parameter pack:
    template <typename... Types> void func1(std::vector<Types...> v1);
    
    // v2 IS a function parameter pack:
    template <typename... Types> void func2(std::vector<Types>... v2);
    
  • 매개 변수 이름 바로 뒤에 줄임표가 표시된 경우 매개 변수 팩 확장이 있는 것입니다.

예시

variadic 함수 템플릿 메커니즘을 설명하는 좋은 방법은 다음과 같은 기능 중 일부를 다시 작성하는 것입니다.printf

#include <iostream>

using namespace std;

void print() {
    cout << endl;
}

template <typename T> void print(const T& t) {
    cout << t << endl;
}

template <typename First, typename... Rest> void print(const First& first, const Rest&... rest) {
    cout << first << ", ";
    print(rest...); // recursive call using pack expansion syntax
}

int main()
{
    print(); // calls first overload, outputting only a newline
    print(1); // calls second overload

    // these call the third overload, the variadic template,
    // which uses recursion as needed.
    print(10, 20);
    print(100, 200, 300);
    print("first", 2, "third", 3.14159);
}

출력

1
10, 20
100, 200, 300
first, 2, third, 3.14159

참고 항목

variadic 함수 템플릿을 통합하는 대부분의 구현은 일부 형식의 재귀를 사용하지만 기존 재귀와는 약간 다릅니다. 기존 재귀에는 동일한 서명을 사용하여 자신을 호출하는 함수가 포함됩니다. (오버로드되거나 템플릿으로 작성될 수 있지만 매번 동일한 서명이 선택됩니다.) Variadic 재귀에는 서로 다른(거의 항상 감소하는) 인수 수를 사용하여 variadic 함수 템플릿을 호출하여 매번 다른 서명을 스탬핑하는 작업이 포함됩니다. "기본 사례"는 여전히 필요하지만 재귀의 특성은 다릅니다.