이상하게 반복되는 템플릿 패턴
Curiously recurring template patternCQRTP(Queryly Recurring Template Pattern)는 C++의 관용어로 클래스가X
를 사용한 클래스 템플릿 인스턴스화에서 파생됩니다.X
템플릿 [1]인수로 지정됩니다.보다 일반적으로 F-결합 다형성으로 알려져 있으며, F-결합 정량화의 한 형태이다.
역사
이 기법은 1989년에 "F-bound quantification"[2]으로 공식화되었습니다."[3]CRTP"라는 이름은 1995년 짐 코플린에 의해 독립적으로 만들어졌으며, 그는 초기 C++ 템플릿 코드 중 일부와 티모시 버드가 그의 다중 문자 변환 언어인 [4]Leda에서 만든 코드 예제에서 이를 관찰했다.다른 기본 클래스를 대체하여 클래스 계층을 확장할 수 있기 때문에 "업사이드-다운 상속"[5][6]이라고도 합니다.
Active Template Library(ATL; 액티브템플릿 라이브러리)에서의 Microsoft CRTP 실장은 1995년에 Jan Falkin에 의해 독립적으로 검출되었습니다.Jan Falkin은 우연히 파생 클래스에서 베이스 클래스를 취득했습니다.Christian Beaumont는 처음에 Jan의 코드를 보았고 처음에는 그것이 그 당시에 사용 가능한 마이크로소프트 컴파일러로 컴파일 할 수 없을 것이라고 생각했다.Christian은 실제로 효과가 있었다는 사실이 밝혀진 후 ATL과 Windows Template Library(WTL) 설계 전체를 이 실수를 [citation needed]바탕으로 했습니다.
일반형식
// Cruously Recurring Template Pattern(CRTP; 이상하게 반복되는 템플릿 패턴) 템플릿 <>학급 T> 학급 기초 { // 베이스 내의 메서드는 템플릿을 사용하여 Derived 멤버에 액세스할 수 있습니다. }; 학급 파생된 : 일반의 기초<>파생된> { // ... };
이 패턴의 사용 예로는 정적 다형성 및 Andrei Alexandrescu가 Modern C++[7] 디자인에서 설명한 기타 메타프로그래밍 기술이 있습니다.또한 데이터,[8] 컨텍스트 및 인터랙션 패러다임의 C++ 구현에서도 중요한 역할을 합니다.
정적 다형
일반적으로 기본 클래스 템플릿은 멤버 함수 본문(정의)이 선언 후까지 인스턴스화되지 않는다는 사실을 이용하여 캐스트를 사용하여 자체 멤버 함수 내에서 파생 클래스의 멤버를 사용합니다.
템플릿 <>학급 T> 구조 기초 { 무효 인터페이스() { // ... static_cast<>T*>(이것.)->실행(); // ... } 정적인 무효 static_func() { // ... T::static_sub_func(); // ... } }; 구조 파생된 : 기초<>파생된> { 무효 실행(); 정적인 무효 static_sub_func(); };
위의 예에서 특히 노트 기능이 Base<, Derived>.::implementation()지만 선언되기 전에 존재의 구조 파생이라고 알려져에 의해 컴파일러(즉, 전에 파생으로 선언되어 있),은 사실 인스턴스화에 의해 컴파일럴 때까지 이것은 실제로라 불리는 효소에 의해는 후속 코드 발생하는 후에 그 선언의 드.rived(위의 예에서는 표시되지 않습니다).따라서 함수 "implementation"이 인스턴스화될 때 Derived::implementation()의 선언을 알 수 있습니다.
이 기술은 동적 다형성의 비용(및 약간의 유연성) 없이 가상 기능의 사용과 유사한 효과를 달성합니다.이러한 CRTP의 특별한 사용을 일부에서는 [9]"시뮬레이트된 동적 바인딩"이라고 부릅니다.이 패턴은 Windows ATL 및 WTL 라이브러리에서 광범위하게 사용됩니다.
위의 예에 대해 자세히 설명하려면 가상 기능이 없는 기본 클래스를 고려하십시오.베이스 클래스는 다른 멤버 함수를 호출할 때마다 항상 자신의 베이스 클래스 함수를 호출합니다.이 기본 클래스에서 클래스를 파생할 때 재정의되지 않은 모든 멤버 변수 및 멤버 함수를 상속합니다(생성자 또는 소멸자 없음).파생 클래스가 상속된 함수를 호출한 후 다른 멤버 함수를 호출하면 해당 함수는 파생 클래스의 파생 멤버 함수 또는 오버라이드 멤버 함수를 호출하지 않습니다.
단, 기본 클래스 멤버 함수가 모든 멤버 함수 호출에 CRTP를 사용하는 경우 파생 클래스의 덮어쓰기 함수는 컴파일 시 선택됩니다.이것에 의해, 사이즈나 함수 콜 오버헤드(VTBL 구조, 및 메서드 룩업, 멀티 상속 VTBL 머신)의 코스트 없이, 컴파일시에 가상 함수 콜시스템을 효과적으로 에뮬레이트 할 수 있습니다.
오브젝트 카운터
오브젝트 카운터의 주요 목적은 특정 [10]클래스에 대한 오브젝트 생성 및 삭제 통계 정보를 검색하는 것입니다.이 문제는 CRTP를 사용하면 쉽게 해결할 수 있습니다.
템플릿 <>타이프네임 T> 구조 계산대 { 정적인 인라인 인트 objects_created = 0; 정적인 인라인 인트 오브젝트_개요 = 0; 계산대() { ++objects_created; ++오브젝트_개요; } 계산대(컨스턴트 계산대&) { ++objects_created; ++오브젝트_개요; } 보호되고 있다: ~계산대() // 이 유형의 포인터를 통해 개체를 제거해서는 안 됩니다. { --오브젝트_개요; } }; 학급 X : 계산대<>X> { // ... }; 학급 Y : 계산대<>Y> { // ... };
클래스의 오브젝트가 될 때마다X
작성, 작성, 의 컨스트럭터입니다.counter<X>
가 호출되어 작성된 카운트와 얼라이브 카운트가 모두 증가합니다.클래스의 오브젝트가 될 때마다X
파괴되면 활성 카운트가 감소합니다.주의할 점은counter<X>
그리고.counter<Y>
두 개의 독립된 클래스입니다.이것이 그들이 개별 카운트를 유지하는 이유입니다.X
및Y
s. 이 CRTP 예에서는 이 클래스의 구별이 템플릿파라미터의 유일한 사용법입니다(T
에counter<T>
) 및 단순한 비압축 베이스 클래스를 사용할 수 없는 이유.
다형사슬
메서드 체인은 이름 있는 파라미터 idi라고도 하며 객체 지향 프로그래밍 언어에서 여러 메서드 호출을 호출하기 위한 일반적인 구문입니다.각 메서드는 오브젝트를 반환하고 중간 결과를 저장하는 변수를 필요로 하지 않고 콜을 단일 스테이트먼트 내에서 체인으로 연결할 수 있습니다.
명명된 매개 변수 개체 패턴이 개체 계층에 적용되면 문제가 발생할 수 있습니다.다음과 같은 기본 클래스가 있다고 가정합니다.
학급 프린터 { 일반의: 프린터(스트림& 스트림) : m_stream(스트림) {} 템플릿 <>타이프네임 T> 프린터& 인쇄물(T& & t) { m_stream << > t; 돌아가다 *이것.; } 템플릿 <>타이프네임 T> 프린터& 인쇄(T& & t) { m_stream << > t << > 끝; 돌아가다 *이것.; } 사적인: 스트림& m_stream; };
인쇄는 간단하게 체인 접속할 수 있습니다.
프린터(myStream).인쇄("안녕하세요").인쇄(500);
단, 다음과 같은 파생 클래스를 정의하는 경우:
학급 Cout Printer : 일반의 프린터 { 일반의: Cout Printer() : 프린터(외치다) {} Cout Printer& Set Console 색상(색. c) { // ... 돌아가다 *이것.; } };
베이스의 함수를 호출하는 즉시 콘크리트 클래스가 "소실"된다.
// v----- 여기에는 'Cout Printer'가 아닌 'Printer'가 있습니다. Cout Printer().인쇄물("안녕하세요").Set Console 색상(색..빨간.).인쇄("프린터!"); // 컴파일 오류
이는 '인쇄'가 기본 함수인 '프린터'의 함수이며 '프린터' 인스턴스를 반환하기 때문입니다.
CRTP를 사용하여 이러한 문제를 방지하고 "폴리모픽 체인"[11]을 구현할 수 있습니다.
// 베이스 클래스 템플릿 <>타이프네임 콘크리트 프린터> 학급 프린터 { 일반의: 프린터(스트림& 스트림) : m_stream(스트림) {} 템플릿 <>타이프네임 T> 콘크리트 프린터& 인쇄물(T& & t) { m_stream << > t; 돌아가다 static_cast<>콘크리트 프린터&>(*이것.); } 템플릿 <>타이프네임 T> 콘크리트 프린터& 인쇄(T& & t) { m_stream << > t << > 끝; 돌아가다 static_cast<>콘크리트 프린터&>(*이것.); } 사적인: 스트림& m_stream; }; // 파생 클래스 학급 Cout Printer : 일반의 프린터<>Cout Printer> { 일반의: Cout Printer() : 프린터(외치다) {} Cout Printer& Set Console 색상(색. c) { // ... 돌아가다 *이것.; } }; // 사용방법 Cout Printer().인쇄물("안녕하세요").Set Console 색상(색..빨간.).인쇄("프린터!");
다형 복사 구성
다형성을 사용할 때 기본 클래스 포인터로 오브젝트 복사본을 만들어야 하는 경우가 있습니다.이를 위해 일반적으로 사용되는 관용어는 파생된 모든 클래스에 정의된 가상 복제 함수를 추가하는 것입니다.CRTP를 사용하면 파생 클래스마다 해당 함수 또는 기타 유사한 함수가 중복되지 않도록 할 수 있습니다.
// 기본 클래스에는 복제를 위한 순수 가상 함수가 있습니다. 학급 추상형 { 일반의: 가상 ~추상형 () = 체납; 가상 표준::unique_ptr<>추상형> 클론() 컨스턴트 = 0; }; // 이 CRTP 클래스는 Derived에 대해 clone()을 구현합니다. 템플릿 <>타이프네임 파생된> 학급 모양. : 일반의 추상형 { 일반의: 표준::unique_ptr<>추상형> 클론() 컨스턴트 덮어쓰다 { 돌아가다 표준::make_filengths<>파생된>(static_cast<>파생된 컨스턴트&>(*이것.)); } 보호되고 있다: // 쉐이프 클래스를 상속할 필요가 있음을 명확히 합니다. 모양.() = 체납; 모양.(컨스턴트 모양.&) = 체납; 모양.(모양.& &) = 체납; }; // 파생된 모든 클래스는 추상 클래스가 아닌 CRTP 클래스에서 상속됩니다. 학급 광장 : 일반의 모양.<>광장>{}; 학급 원형 : 일반의 모양.<>원형>{};
이를 통해 정사각형, 원 또는 기타 도형의 복사본을 얻을 수 있습니다.shapePtr->clone()
.
함정
정적 다형성의 한 가지 문제는 다음과 같은 일반적인 기본 클래스를 사용하지 않는다는 것입니다.AbstractShape
위의 예에서 파생된 클래스는 균일하게 저장할 수 없습니다. 즉, 동일한 베이스 클래스에서 파생된 다른 유형을 동일한 용기에 넣습니다.예를 들어 다음과 같이 정의된 컨테이너입니다.std::vector<Shape*>
동작하지 않는 이유는Shape
클래스가 아니라 전문화가 필요한 템플릿입니다.다음과 같이 정의된 컨테이너std::vector<Shape<Circle>*>
저장만 가능Circle
s, 아니다Square
s. 이는 각 클래스가 CRTP 기본 클래스에서 파생되었기 때문입니다.Shape
고유한 유형입니다.이 문제에 대한 일반적인 해결책은 가상 소멸자를 사용하여 공유 기본 클래스에서 상속하는 것입니다.AbstractShape
위의 예에서는, 의 작성을 허가하고 있습니다.std::vector<AbstractShape*>
.
「 」를 참조해 주세요.
레퍼런스
- ^ Abrahams, David; Gurtovoy, Aleksey (January 2005). C++ Template Metaprogramming: Concepts, Tools, and Techniques from Boost and Beyond. Addison-Wesley. ISBN 0-321-22725-5.
- ^ William Cook; et al. (1989). "F-Bounded Polymorphism for Object-Oriented Programming" (PDF).
- ^ Coplien, James O. (February 1995). "Curiously Recurring Template Patterns" (PDF). C++ Report: 24–27.
- ^ Budd, Timothy (1994). Multiparadigm programming in Leda. Addison-Wesley. ISBN 0-201-82080-3.
- ^ "Apostate Café: ATL and Upside-Down Inheritance". 15 March 2006. Archived from the original on 15 March 2006. Retrieved 9 October 2016.
{{cite web}}
: CS1 maint: bot: 원래 URL 상태를 알 수 없습니다(링크). - ^ "ATL and Upside-Down Inheritance". 4 June 2003. Archived from the original on 4 June 2003. Retrieved 9 October 2016.
{{cite web}}
: CS1 maint: bot: 원래 URL 상태를 알 수 없습니다(링크). - ^ Alexandrescu, Andrei (2001). Modern C++ Design: Generic Programming and Design Patterns Applied. Addison-Wesley. ISBN 0-201-70431-5.
- ^ Coplien, James; Bjørnvig, Gertrud (2010). Lean Architecture: for agile software development. Wiley. ISBN 978-0-470-68420-7.
- ^ "Simulated Dynamic Binding". 7 May 2003. Archived from the original on 9 February 2012. Retrieved 13 January 2012.
- ^ Meyers, Scott (April 1998). "Counting Objects in C++". C/C++ Users Journal.
- ^ Arena, Marco (29 April 2012). "Use CRTP for polymorphic chaining". Retrieved 15 March 2017.