가변 템플릿

Variadic template

컴퓨터 프로그래밍에서 가변 템플릿은 변수 개수의 인수를 사용하는 템플릿입니다.

Variadic 템플릿은 C++(C++11 표준 이후) 및 D 프로그래밍 언어로 지원됩니다.

C++

C++의 다양한 템플릿 기능은 Douglas Gregor와 Jaakko Jérvi에 의해 설계되었으며 이후 C++11에서 표준화되었습니다.C++11 이전에는 템플릿(클래스 및 함수)은 템플릿이 처음 선언되었을 때 지정해야 했던 고정된 수의 인수만 사용할 수 있었습니다.C++11을 사용하면 템플릿 정의에서 임의의 유형의 인수를 임의로 사용할 수 있습니다.

템플릿< >타이프네임... 가치> 학급 태플;               // 0 이상의 인수를 받아들인다. 

위의 템플릿클래스tuple는 임의의 수의 타이프네임을 템플릿파라미터로 사용합니다.여기서 위의 템플릿클래스의 인스턴스는 다음 3가지 유형의 인수를 사용하여 인스턴스화됩니다.

태플< >인트, 표준::벡터< >인트>, 표준::지도< >표준::스트링, 표준::벡터< >인트>>> some_model_name; 

인수 수는 0으로 할 수 있습니다.tuple<> some_instance_name;동작합니다.

variadic 템플릿이 양의 인수만 허용해야 하는 경우 다음 정의를 사용할 수 있습니다.

템플릿< >타이프네임 첫번째, 타이프네임... 쉬다> 학급 태플; // 하나 이상의 인수를 사용합니다. 

variadic 템플릿은 함수에도 적용될 수 있으므로 variadic 함수(printf 등)에 타입 세이프 애드온을 제공할 뿐만 아니라 printf와 같은 구문을 사용하여 호출된 함수를 통해 중요하지 않은 객체를 처리할 수 있습니다.

템플릿< >타이프네임... 파라메스> 무효 my_printf(컨스턴트 표준::스트링 &str_format, 파라메스... 파라미터); 

줄임표(...) 연산자에는 두 가지 역할이 있습니다.매개 변수 이름 왼쪽에 있으면 매개 변수 팩을 선언합니다.파라미터 팩을 사용하여 사용자는 변수 템플릿파라미터에 0 이상의 인수를 바인드할 수 있습니다.파라미터 팩은 타입이 아닌 파라미터에도 사용할 수 있습니다.반면 생략 부호 연산자는 템플릿 또는 함수 호출 인수의 오른쪽에 발생하면 파라미터 팩을 별도의 인수로 전개합니다.args...의 몸으로printf아래. 실제로 코드에서 줄임표 연산자를 사용하면 인수 팩에서 압축 해제된 후속 인수마다 줄임표 앞에 오는 식 전체가 쉼표로 구분되어 반복됩니다.

가변 템플릿의 사용은 종종 재귀적입니다.가변 매개변수 자체는 함수 또는 클래스의 구현에 쉽게 사용할 수 없습니다.따라서 C++11 바리에다드 등의 것을 정의하기 위한 일반적인 메커니즘입니다.printf대체 방법은 다음과 같습니다.

// 베이스 케이스 무효 my_printf(컨스턴트  *s) {     하는 동안에 (*s)     {         한다면 (*s == '%')         {             한다면 (*(s + 1) != '%')                 ++s;             또 다른                 던지다 표준::runtime_error(실행시 오류)("오류 형식 문자열: 인수 누락");         }          표준::외치다 << > *s++;     } }  // 재귀적 템플릿< >타이프네임 T, 타이프네임... 아르그> 무효 my_printf(컨스턴트  *s, T 가치, 아르그... args) {     하는 동안에 (*s)     {         한다면 (*s == '%')         {             한다면 (*(s + 1) != '%')             {                 // 형식을 해석하는 척: 2글자 형식 문자열( %d, %f 등)에서만 작동하며 %5.4f에서는 실패합니다.                 s += 2;                 // 값 인쇄                 표준::외치다 << > 가치;                 // *s가 0인 경우에도 호출되지만 이 경우 아무것도 수행하지 않음(추가 인수 무시)                 my_printf(s, args...);                 돌아가다;             }              ++s;         }          표준::외치다 << > *s++;     }     } 

이것은 재귀 템플릿입니다.의 가변 템플릿버전이my_printf콜 자체 또는 (이 경우)args...is empty)가 베이스 케이스를 호출합니다.

가변 템플릿의 값을 반복하는 간단한 메커니즘은 없습니다.단, 인수 팩을 파라미터별로 개별적으로 평가할 수 있는 단일 인수로 변환하는 방법은 여러 가지가 있습니다.일반적으로 이것은 함수의 오버로드에 의존합니다.또는 함수가 한 번에 하나의 인수를 선택할 수 있는 경우에는 덤 확장 마커를 사용합니다.

템플릿< >타이프네임... 아르그> 인라인 무효 통과하다(아르그& &...) {} 

다음과 같이 사용할 수 있습니다.

템플릿< >타이프네임... 아르그> 인라인 무효 확대한다.(아르그& &... args) {     통과하다(기능(args)...); }  확대한다.(42, "답변", 진실의); 

다음과 같이 확장됩니다.

    통과하다(기능(arg1), 기능(arg2), 기능(arg3) /* 등...*/ ); 

인수 팩의 확장은 쉼표로 함수 호출 인수를 구분하는 것으로 진행되기 때문에 이 "pass" 함수의 사용이 필요합니다.이것은 쉼표 연산자와 동등하지 않습니다.그러므로,some_function(args)...;절대 안 먹힐 거야또한 위의 솔루션은 반환 유형이 다음과 같은 경우에만 작동합니다.some_function아니다void또,some_function함수 인수의 평가 순서가 정의되어 있지 않기 때문에 콜은 지정되지 않은 순서로 실행됩니다.지정되지 않은 순서를 피하기 위해 괄호로 둘러싸인 이니셜라이저 리스트를 사용할 수 있습니다.이 리스트는 엄격한 왼쪽에서 오른쪽으로의 평가 순서를 보증합니다.이니셜라이저 목록에는 다음이 필요합니다.void반환 유형이지만 쉼표 연산자를 사용하여 값을 산출할 수 있습니다.1각 확장 요소에 대해.

구조 통과하다 {     템플릿< >타이프네임 ...T> 통과하다(T...) {} };  통과하다{(기능(args), 1)...}; 

함수를 실행하는 대신 람다 식을 지정해 그 자리에서 실행할 수 있으며, 이를 통해 임의의 스테이트먼트 시퀀스를 그 자리에서 실행할 수 있다.

pass {([&](){ std::cout < args << std::endl; }( , 1)...};

그러나 이 예제에서는 람다 함수가 필요하지 않습니다.대신 보다 일반적인 표현을 사용할 수 있습니다.

pass{(std::cout< args< std::endl, 1)...};

또 다른 방법은 기능의 "종료 버전"에서 오버로드를 사용하는 것입니다.이것은 보다 보편적이지만, 만드는 데 좀 더 많은 코드와 노력이 필요합니다.한쪽 함수는 어떤 타입의 인수와 인수 팩을 수신하지만 다른 한쪽 함수는 둘 다 수신하지 않습니다(양쪽 모두 초기 파라미터 목록이 같을 경우 콜은 모호해집니다.바리에이티브 파라미터 팩만으로는 콜을 명확하게 할 수 없습니다).예를 들어 다음과 같습니다.

무효 기능하다() {} // 종료 버전  템플릿< >타이프네임 Arg1, 타이프네임... 아르그> 무효 기능하다(컨스턴트 Arg1& arg1, 컨스턴트 아르그& &... args) {     과정( arg1 );     기능하다(args...); // 주의: arg1은 여기에 표시되지 않습니다! } 

한다면args...에는 적어도1개의 인수가 포함되어 있습니다.두 번째 버전으로 리다이렉트 됩니다.파라미터 팩은 비워 둘 수 있습니다.이 경우 종료 버전으로 리다이렉트하기만 하면 아무것도 실행되지 않습니다.

예외 사양, 기본 클래스 목록 또는 생성자의 초기화 목록에서도 가변 템플릿을 사용할 수 있습니다.예를 들어 클래스는 다음을 지정할 수 있습니다.

템플릿 < >타이프네임... 베이스 클래스> 학급 클래스명 : 일반의 베이스 클래스... { 일반의:     클래스명 (베이스 클래스& &... base_module)         : 베이스 클래스(base_module)...     {} }; 

언팩 연산자는 다음 기본 클래스의 유형을 복제합니다.ClassName이 클래스는 전달된 각 유형에서 파생됩니다.또한 생성자는 기본 클래스를 초기화하기 위해 각 기본 클래스를 참조해야 합니다.ClassName.

함수 템플릿에 대해서는 바리에딕 파라미터를 전송할 수 있습니다.범용 레퍼런스(상기 참조)와 조합하면, 다음과 같이 완벽한 전송이 가능하게 됩니다.

템플릿< >타이프네임 Type To Construct(유형 구성)> 구조 SharedPtrAllocator {     템플릿< >타이프네임 ...아르그>     표준::shared_ptr< >Type To Construct(유형 구성)> construct_with_shared_ptr(아르그& &... 파라미터)     {         돌아가다 표준::shared_ptr< >Type To Construct(유형 구성)>(신규 Type To Construct(유형 구성)(표준::앞으로< >아르그>(파라미터)...));     } }; 

그러면 인수 목록이 TypeToConstruct의 컨스트럭터에 언팩됩니다.std::forward<Args>(params)구문은 rvalue-ness에 관한 인수라도 적절한 유형으로 컨스트럭터에 완벽하게 전송합니다.언팩 연산자는 전송 구문을 각 파라미터에 전파합니다.이 특정 공장 출하시 기능은 할당된 메모리를 자동으로 랩핑합니다.std::shared_ptr메모리 누전에 관한 안전성의 정도를 확인합니다.

또한 템플릿파라미터 팩의 인수 수는 다음과 같이 결정할 수 있습니다.

템플릿< >타이프네임 ...아르그> 구조 일부 구조 {     정적인 컨스턴트 인트 크기 = 크기...(아르그); }; 

표현SomeStruct<Type1, Type2>::size2를 얻을 수 있지만SomeStruct<>::size0이 됩니다.

D

정의.

D의 바리에이딕템플릿 정의는 C++와 비슷합니다.

템플릿 Varadic Template(아르그...) { /* 본문 */ } 

마찬가지로 인수 목록 앞에는 임의의 인수를 사용할 수 있습니다.

템플릿 Varadic Template(T, 스트링 가치, 에일리어스 기호., 아르그...) { /* 본문 */ } 

기본 사용법

variadic 인수는 그 사용방법에 있어서 상수 배열과 매우 유사합니다.반복할 수 있고, 인덱스에 의해 액세스되며,length슬라이스할 수 있습니다.연산은 컴파일 시에 해석됩니다.즉, 피연산자는 런타임 값(함수 파라미터 등)이 될 수 없습니다.

컴파일 시에 알려진 모든 것은 가변 인수로 전달될 수 있습니다.템플릿 에일리어스 인수와 비슷하지만 기본 유형(char, short, int...)도 받아들이기 때문에 variadic 인수가 더욱 강력해집니다.

다음으로 가변 파라미터의 문자열 표현을 인쇄하는 예를 나타냅니다. StringOf그리고.StringOf2대등한 결과를 낳다

정적인 인트 s_int;  구조 모조의 {}  무효 주된() {   플러그마(메시지, String Of(문자열 Of)!('헬로 월드', 설치하다, 모조의, 42, s_int));   플러그마(메시지, String Of2!('헬로 월드', 설치하다, 모조의, 42, s_int)); }  템플릿 String Of(문자열 Of)(아르그...) {   열거하다 String Of(문자열 Of) = 아르그[0].스트링 ~ String Of(문자열 Of)!(아르그[1..$]); }  템플릿 String Of(문자열 Of)() {   열거하다 String Of(문자열 Of) = ""; }  템플릿 String Of2(아르그...) {   정적인 한다면 (아르그.길이 == 0)     열거하다 String Of2 = "";   또 다른     열거하다 String Of2 = 아르그[0].스트링 ~ String Of2!(아르그[1..$]); } 

출력:

"Hello world" uint Dummy42s_int "Hello world" uint Dummy42s_int

AliasSeq

AliasSeq라는 이름의 에일리어스 시퀀스를 작성하기 위해 바리에딕템플릿이 자주 사용됩니다.AliasSeq의 정의는 실제로 매우 간단합니다.

에일리어스 AliasSeq(아르그...) = 아르그; 

이 구조를 사용하면 자동으로 확장되는 변수 목록을 조작할 수 있습니다.인수는 컴파일 시 알려진 기호 또는 값이어야 합니다.여기에는 값, 유형, 함수 또는 전문적이지 않은 템플릿도 포함됩니다.이것에 의해, 다음과 같은 조작이 가능하게 됩니다.

수입품 표준.메타;  무효 주된() {   // 주의:AliasSeq는 수정할 수 없고 에일리어스는 리바운드할 수 없으므로 수정에 대한 새 이름을 정의해야 합니다.   에일리어스 숫자 = AliasSeq!(1, 2, 3, 4, 5, 6);   // 슬라이스   에일리어스 후반부 = 숫자[$ / 2 .. $];   정적인 주장하다(후반부 == AliasSeq!(4, 5, 6));   // AliasSeq 자동 확장   에일리어스 숫자 = AliasSeq!(0, 숫자, 7, 8, 9);   정적인 주장하다(숫자 == AliasSeq!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9));   // std.meta는 AliasSeq와 연동되는 템플릿을 제공합니다.예를 들어 anySatisfy, allSatisfy, staticMap, Filter 등이 있습니다.   에일리어스 짝수 = 필터!(이븐, 숫자);   정적인 주장하다(짝수 == AliasSeq!(0, 2, 4, 6, 8)); }  템플릿 이븐(인트 번호) {   열거하다 이븐 = (0 == (번호 % 2)); } 

「 」를 참조해 주세요.

템플릿 이외의 다양한 구조에 대한 기사

레퍼런스

  1. ^ Douglas Gregor & Jaakko Järvi (February 2008). "Variadic Templates for C++0x". Journal of Object Technology. pp. 31–51.
  2. ^ Douglas Gregor; Jaakko Järvi & Gary Powell. (February 2004). "Variadic templates. Number N1603=04-0043 in ISO C++ Standard Committee Pre-Sydney mailing".

외부 링크