관련 문서: C++ #!if 문서명2 != null
, [[C++/문법/템플릿]]
#!if 문서명3 != null
, [[C++/문법/상수 표현식]]
#!if 문서명4 != null
, [[]]
#!if 문서명5 != null
, [[]]
#!if 문서명6 != null
, [[]]
상위 문서: C++/문법 1 . 개요2 . 이름있는 필요조건3 . concept3.1 . 템플릿 매개변수에 적용3.2 . 예제1: 클래스 상속 여부 검사3.3 . auto에 적용4 . requires5 . 개념과 requires6 . 템플릿과 requires7 . 예제 2: 클래스 기본 멤버 함수8 . 제약조건의 도입 목적9 . 예제 3: 함자에 튜플로 인자 전달하기10 . 예제 4: 선택적 함자 실행11 . 표준 개념 모듈 제약조건과 개념 C++ 의 이름있는 제약조건, C++20
에서 추가된 requires
표현식, 개념 (Concept)에 대하여 설명하는 문서. C++ 템플릿 문서에서 바로 연계되는 내용을 담고 있으므로 선행 학습이 필요하다. 제약조건의 연산 결과 자체는 bool
을 반환하는 하나의 상수 표현식 템플릿 이다. 상수 표현식 템플릿은 template<typename T> constexpr bool value = ...;
처럼 정의하는 데 제약조건도 비슷하게 정의한다. 제약조건은 템플릿에 사용되는 자료형에 대하여 어떤 조건을 검사하는 독립된 표현식이거나, 어떤 함수나 클래스 템플릿에 달린 조건일 수 있다. 이 문서에서 설명하는 내용은 더욱 더 안전하고 정확한 동작을 보장하는 일반화 프로그래밍, 그리고 간편하고 읽기 좋은 메타 프로그래밍을 위한 것이다. 사실 C++20
이전이라면 메타 프로그래밍 문서에서 더 다양한 SFINAE 예시와 함께 자료형 검사와 조작에 관한 내용도 있어야겠지만, 이젠 새로운 언어 기능이 추가됐으므로 내용을 분리한다. C++ 메타 프로그래밍 문서에서 템플릿 제약조건의 이론적 배경 및 내부 구현을 설명하고 여기에서는 문법과 예제 위주로 서술한다.2. 이름있는 필요조건이름있는 필요조건 (Named Requirements) C++에는 아주 예전부터 이름있는 필요조건이라 하여 템플릿 매개변수로 쓰일 수 있는 값들을 제약하는 사양이 표준으로 있었다. 표준 명세대로 템플릿 코드를 구현했으면 당연히 이 조건을 따를 것이라고 믿는 것이었다. 하지만, 이는 표준에서 템플릿에 쓰이는 인자가 이러한 조건을 만족해야 한다는 권고사항에 가까웠다. 강제할만한 문법도 없었으니 말이다. 그나마 C++11
에 추가된 static_assert
로 어느정도 문법 기능 부족은 해소가 되었으나 사용자한테만 오류라고 띄우는 거고, 문법 오류가 아니라서 SFINAE로 연계를 할 수가 없어서 자료형 연산은 불가능한 상황이었다. <rowcolor=#d7d7d7,#a1a1a1>이름 설명 표준 <rowcolor=#090912,#bebebf>DefaultConstructible {}
또는 ()
로 인스턴스를 생성 할 수 있는 자료형. 당연히 원시 자료형도 포함된다.# <rowcolor=#090912,#bebebf>ScalarType (스칼라 유형) 숫자 , 열거형, 포인터 , 클래스 의 멤버의 포인터, nullptr
등 순수 숫자로 표현할 수 있는 자료형과 이에 대한 배열이 포함된다.# <rowcolor=#090912,#bebebf>TriviallyCopyable (자명하게 복사가능함) # <rowcolor=#090912,#bebebf>FunctionObject (함수 객체) # <rowcolor=#090912,#bebebf>EqualityComparable (동등하게 비교가능함) ==
연산자를 수행했을때, 값으로 동등 비교를 수행하여 같으면 true
, 다르면 false
를 반환하는 자료형.자료형의 값은 const
이든 아니든 상관없어야 한다 . 여기서 ==
연산자는 동일한(Equality) 것이든, 동치인(Equivalence) 것이든, 오버로딩된 것이라도 상관없고 bool
을 반환하면 된다 . # <rowcolor=#090912,#bebebf>BasicLockable 기본 잠금 동작이 가능함) 멤버 함수로 lock()
, unlock()
을 가지는 클래스. # <rowcolor=#090912,#bebebf>Lockable (잠금 동작 및 시도가 가능함) BasicLockable 을 만족하는 클래스.멤버 함수로 try_lock() ->
bool
을 가지는 클래스. #
이 명세의 일부는 C++11
에서 메타 함수로 제공되고 있다. 또한 몇몇은 다음에 소개할 concept
기능으로 정식 표준 제약조건으로 제정되었다. Clock 이라든가, <mdspan>
에 쓰이는 LayoutMapping 라든가 C++20
부터는 필요시 이름있는 필요조건과 제약조건이 동시에 제정되고 있다. 3. concept [include(틀:C++ 요소, head_keyword=template, template_p0=개념-자료형, template_p1=템플릿-매개변수, template_last_label=...)] [include(틀:C++ 요소, head_keyword=concept, pre1_t=개념-식별자, label_last= \= 상수 bool 값, last=;)]
개념 (Concept) C++20 개념 또는 컨셉트 (Concept)는 템플릿 매개변수에 식별자와 함께 조건을 붙일 수 있는 기능이다. 템플릿과 auto
에 들어오는 자료형이 개념에 정의한 조건을 만족하는지 컴파일 시점에 검사한다. 개념도 하나의 템플릿 객체이기에 템플릿을 써서 정의한다. 개념은 어떤 클래스나 함수 안에 정의할 수 없다. 즉 C++ 객체에 의존성을 가지면 안된다. 정적 지속요건(Static Duration)을 가져야 한다. 무슨 뜻이냐 하면, 전역 범위(Scope) 나 이름공간 에만 정의할 수 있다는 뜻이다.<C++ 예제 보기> #!syntax cpp
import <print>;
import <type_traits>;
// C++11의 상수 표현식 템플릿을 그대로 차용
template<typename T>
concept integrals = std::is_integral_v<T>;
template<typename T>
void Print(T&& value)
{
std::println("정수가 아닌 값 {} 출력", std::forward<T>(value));
}
// 후방에 선언되어 있어도 정수인 경우를 잘 걸러낼 수 있다.
template<integrals T>
// 개념의 첫번째 템플릿 매개변수가 생략된다.
// 이 경우 `T`가 개념 `integrals`의 첫번째 매개변수로 들어간다.
void Print(T&& value)
{
std::println("정수 값 {} 출력", std::forward<T>(value));
}
int main()
{
// (1)
// C++11의 메타 함수를 사용한 static_assert
static_assert(std::is_integral_v<bool>);
static_assert(std::is_integral_v<int>);
static_assert(std::is_integral_v<unsigned int>);
static_assert(std::is_integral_v<long long>);
static_assert(std::is_integral_v<float>); // 컴파일 오류!
static_assert(std::is_integral_v<double>); // 컴파일 오류!
// (2)
// C++20의 제약 조건을 사용한 static_assert
static_assert(integrals<bool>);
static_assert(integrals<char>);
static_assert(integrals<int>);
static_assert(integrals<unsigned int>);
static_assert(integrals<long long>);
static_assert(integrals<float>); // 컴파일 오류!
static_assert(integrals<double>); // 컴파일 오류!
// (3)
// 템플릿에서 제약 조건 사용
// (3-1)
// "정수 값 10000 출력"
Print(10'000);
// (3-2)
// "정수 값 10000000 출력"
Print(10'000'000ULL);
// (3-3)
// "정수가 아닌 값 Hello, world! 출력"
Print("Hello, world!");
// (3-4)
// "정수가 아닌 값 nullptr 출력"
Print(nullptr);
}
이 기능은 기존에 있던 변수 템플릿 기능을 어법적으로 확장한 것이다. 그래서 개념 구문 자체는 bool
을 반환하는 상수 표현식 템플릿이며 컴파일 시점에 그 값을 알 수 있다. 예를 들어서 개념을 조건문 이나 noexcept(condition)
등에도 쓸 수 있다. 상기 코드는 상수 표현식으로써 개념이 어떤 의미인지 보여주고 있다.3.1. 템플릿 매개변수에 적용 [include(틀:C++ 요소, head_keyword=template, template_cpt0=개념-식별자, template_concept0_p0=템플릿-매개변수1, template_concept0_p1=템플릿-매개변수2, template_concept0_p2=템플릿-매개변수3, template_concept0_last_label=\, ..., template_p0=템플릿-매개변수, template_last_label=..., last=;)]
템플릿에 직접적으로 개념을 사용할 수 있다. 템플릿 매개변수에 typename
대신 사용하거나, 혹은 개념-식별자
auto
처럼 auto
와 조합할 수 있다. 개념이 적용된 템플릿 매개변수는 개념에 정의한 내용을 하나도 빠짐없이 만족해야 한다. 실제 사례를 봐야 이해가 쉬우므로 함수와 클래스 템플릿의 예시를 들어보자.#!syntax cpp
template<typename T>
concept AlwaysTrue = true;
template<AlwaysTrue U>
void Function();
// (1) `AlwaysTrue`에 전달되는 `T`는 `U`와 같음.
// `U`는 int
// `T`도 int
// 함수 서명은 void()
// 템플릿 명세는 template<int>
// 함수 템플릿 인스턴스는 void Function<int>()
Function<int>();
// (2) `AlwaysTrue`에 전달되는 `T`는 `U`와 같음.
// `U`는 std::string
// `T`도 std::string
// 함수 서명은 void()
// 템플릿 명세는 template<std::string>
// 함수 템플릿 인스턴스는 void Function<std::string>()
Function<std::string>();
// (3) `AlwaysTrue`에 전달되는 `T`는 `U`와 같음.
// `U`는 long*
// `T`도 long*
// 함수 서명은 void()
// 템플릿 명세는 template<long*>
// 함수 템플릿 인스턴스는 void Function<long*>()
Function<long[]>();
// (4) `AlwaysTrue`에 전달되는 `T`는 `U`와 같음.
// `U`는 double&
// `T`도 double&
// 함수 서명은 void()
// 템플릿 명세는 template<double&>
// 함수 템플릿 인스턴스는 void Function<double&>()
Function<double&>();
문법적 설탕으로 개념의 첫번째 템플릿 매개변수는 typename
을 바꾼 그 템플릿 매개변수가 들어온다. 상기 예제에선 함수 템플릿의 첫번째 매개변수에 개념 `AlwaysTrue`
를 썼다. 이때 개념 `AlwaysTrue`
의 첫번째 템플릿 매개변수는 함수 `Function`
의 템플릿 매개변수가 대입된다. 다음은 다수의 템플릿 매개변수를 쓴 예시를 보자.#!syntax cpp
template<typename T, typename U>
concept Constructible = std::is_constructible_v<T, U>;
상기 코드는 두 자료형 `T`
와 `U`
를 받아서 `U`
로 `T`
를 생성할 수 있는지 검사하는 개념을 보여주고 있다. 가령 Constructible<int, float>
은 true
, Constructible<std::string, const char*>
은 true
, Constructible<std::vector<int>, std::nullptr_t>
는 false
다.#!syntax cpp
template<typename V, Constructible<V> U>
[[nodiscard]]
U Construct(V argument) noexcept(std::is_nothrow_constructible_v<U, V>)
{
// 집결 초기화 (Aggregate Initialization) 수행
return U { argument };
}
상기 코드는 `V`
의 값을 매개변수로 받아서 `U`
의 인스턴스를 생성하고 반환하는 함수다. 이 함수는 템플릿에서 `U`
의 생성 가능 여부를 확인하기 때문에 컴파일 단계에서 오류를 검출한다.#!syntax cpp
// (1) double을 unsigned int로 생성할 수 있는지 검사
// 문제없음. noexcept(true)
double result0 = Construct<unsigned int, double>(100UL);
상기 코드는 원시 자료형 사이에 사용된 예시를 보여주고 있다.#!syntax cpp
// (2) std::string을 const char*로 생성할 수 있는지 검사
// 문제없음. noexcept(false)
// 그런데 여기서 부패 현상이 일어나서 `const (char&)[14]`가 `const char*`로 바뀌었다.
std::string result1 = Construct<const char*, std::string>("Hello, world!");
// `check`의 값은 true
constexpr bool check = Constructible<std::string, const char[100]>;
여기서 알 수 있는 사실은 개념을 써도 부패는 일어난다. 개념은 함수 매개변수를 한번 부패시키고, 그 다음에 검사를 한다. 이게 싫으면 완벽한 전달을 해야한다. 그런데 개념 자체에 완벽한 전달을 쓰는 건 그다지 추천하지 않는다. 참조자와 한정자는 함수에 들어온 어떤 값에 관한 사항이지, 어떤 값의 메타 정보와는 큰 상관이 없다. 참조자가 있든 없든, 한정자가 있든 없든 값을 읽는 건 개념의 역할이 아니다. 도리어 참조자와 한정자까지 검사하는 건 과도한 참견에 가깝다.#!syntax cpp
struct Integer { int value; };
// (3) Integer를 long으로 생성할 수 있는지 검사
// 문제없음. noexcept(true)
Integer result2 = Construct<long, Integer>(100L);
상기 코드는 사용자 정의 자료형을 검사하는 사례를 보여주고 있다.#!syntax cpp
struct Squirrel
{
Squirrel(long age);
// 다람쥐는 복사 불가능함.
Squirrel(const Squirrel&) = delete;
Squirrel& operator=(const Squirrel&) = delete;
// 다람쥐는 이동 가능함.
Squirrel(Squirrel&&) = default;
Squirrel& operator=(Squirrel&&) = default;
};
// (4) Squirrel를 long으로 생성할 수 있는지 검사
// 문제없음.
// noexcept(false) (Squirrel의 생성자가 noexcept(false)이므로)
Squirrel result3 = Construct<long, Squirrel>(3);
// (5) Squirrel를 Squirrel&&로 생성할 수 있는지 검사
// 문제없음.
// noexcept(true)
Squirrel result4 = Construct<Squirrel&&, Squirrel>(Squirrel{ 6 });
// (6) Squirrel를 Squirrel&로 생성할 수 있는지 검사
// 컴파일 오류! 인수 목록이 일치하는 함수 인스턴스가 없습니다.
// 템플릿 매개변수가 `Constructible`을 만족하지 않음.
Squirrel result5 = Construct<Squirrel&, Squirrel>(result4);
상기 코드는 생성자가 있고 복사 불가능한 사용자 정의 자료형을 검사하는 사례를 보여주고 있다. 이런 식으로 컴파일 단계에서 자료형에 관한 검사를 모두 수행할 수 있다.3.2. 예제1: 클래스 상속 여부 검사#!syntax cpp
struct PipelineObject
{
virtual ~PipelineObject() = default;
virtual bool Awake() = 0;
virtual bool Start() = 0;
virtual void Update(const float dt) = 0;
virtual void PreRender() = 0;
virtual void Destroy() = 0;
virtual void Cleanup() = 0;
protected:
PipelineObject() = default;
};
struct Entity : PipelineObject
{
virtual void Render() = 0;
};
struct GameEntity : Entity {};
struct GameScene : PipelineObject {};
상기 코드는 게임 에 쓰기 위하여 객체 구조를 정의한 것이다. 최상위에 파이프라인 객체, 하위에는 렌더링 할 수 있는 엔티티, 게임 엔티티, 게임 씬 클래스가 자리하고 있다. 이때 게임에 쓰일 함수는 여러가지가 있겠지만 당연히 게임 클래스만 전달할 수 있어야 할 것이다. 기존에 템플릿을 썼다면 아무 자료형이나 넣을 수 있어서 실수를 하거나 잘못된 로직이 작성될 위험이 있었다. 그리고 이걸 파악할 수 있는 방법도 SFINAE 노가다 뿐이었다.#!syntax cpp
template<typename T>
concept GameEntities = std::derived_from<std::remove_cvref_t<T>, GameEntity>;
template<typename T>
concept GameScenes = std::derived_from<std::remove_cvref_t<T>, GameScene>;
template<GameEntities O>
[[nodiscard]]
auto CreateGameEntity(float x = 0.0f, float y = 0.0f);
template<GameEntities O>
[[nodiscard]]
auto CopyGameEntity(const O& original, float x, float y);
template<GameEntities O>
[[nodiscard]]
auto CopyGameEntity(O&& original, float x, float y);
template<GameScenes S>
[[nodiscard]]
S DuplicateGameScene(const S& scene);
이런 식으로 컴파일 시점에 안전한 함수를 만들 수 있다. 개념의 도움이 없으면 파생 클래스에 대하여 오버로딩을 모조리 해주던가, SFINAE 노가다를 모든 파생 클래스에게 해줘야 한다.3.3. auto에 적용 [include(틀:C++ 요소, head_keyword=template, template_p0=템플릿-매개변수1, template_p1=템플릿-매개변수2, template_last_label=..., template_lnb=1, fn_attribute=특성, fn_attribute_lnb=1, pre1_t=반환-자료형, body_f=함수-식별자, arg1_concept=개념-식별자, arg1_concept_params=1, arg1_concept_tparam1=개념-템플릿-매개변수1, arg1_concept_tparam2=개념-템플릿-매개변수2, arg1_concept_tparam3=개념-템플릿-매개변수3, arg1_t_kw=auto, arg1_t_post=[매개변수-참조자], arg1_param=매개변수, arg_last_dots=1, body_bopen=1, body_bclose=1 , last=;)]
함수 템플릿의 매개변수, 변수 템플릿의 정의에 사용할 수 있다. 개념을 auto
와 조합할 수 있도록 돕는 기능이다. 주의할 점은 함수 템플릿의 매개변수에 쓰인 템플릿 매개변수는 부패가 일어나는데 개념을 써도 마찬가지라는 것이다. 개념은 함수 템플릿을 한번 부패시키고 검사하므로 이게 싫으면 &&
를 붙여서 완벽한 전달을 해야한다. 클래스 템플릿은 생성자에 템플릿 매개변수를 쓴 경우가 아니면 부패 문제는 없으므로 안심. 템플릿 대신 auto
와 조합할 수 있으므로 코드의 길이가 짧아지고 작성의 편의성이 상승한다. 그러나 가독성이 상승하는지는 확실치 않다.#!syntax cpp
[[nodiscard]]
auto CopyGameEntity(GameEntities auto&& original, float x, float y);
[[nodiscard]]
auto CopyGameEntity(GameEntities auto& original, float x, float y);
[[nodiscard]]
GameScenes auto DuplicateGameScene(GameScenes auto& scene);
상기 코드는 auto
를 써서 재정의한 생성 함수 템플릿이다.4. requires [include(틀:C++ 요소, head_keyword=template,
template_p0=템플릿-매개변수1, template_p1=템플릿-매개변수2,
template_last_label=...)]
#!if head_comment!=null
{{{#57A64A,#5EBD46 }}}
#!if head_lnb!=null
##======================================= include and import
[br]
#!if import!=null
'''{{{#DodgerBlue,#CornFlowerBlue {{{import }}}}}}'''{{{#C8865E {{{<>}}}}}}{{{;}}}
#!if include!=null
##======================================= the keyword at head (can be `template`)
{{{#include <>}}}
#!if (head_keyword_available = head_keyword != null)
##======================================= template parameters begin
'''{{{#DodgerBlue,#CornFlowerBlue {{{}}}}}}'''
#!if template_available = (template_p0 != null || template_v0 != null || template_p1 != null || template_v1 != null || template_p2 != null || template_v2 != null || template_p3 != null || template_v3 != null)
#!if template_available || template_last_label != null
##======================================= template parameter 0
##======================================= template parameter 0 concept
{{{<}}}{{{#!if template_concept0_available = (template_cpt0 != null)
'''{{{#4ec9b0,#6fdbba {{{ }}}}}}'''}}}{{{#!if template_concept0_params_available = (template_concept0_p0 != null || template_concept0_p1 != null || template_concept0_p2 != null || template_concept0_p3 != null)
{{{#!if template_concept0_params_available || template_concept0_last_label != null
{{{<}}}}}}{{{#!if template_concept0_params_available
##======================================= template parameter 0 concept's parameters (type or value)
{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept0_p0 != null || template_concept0_v0 != null) && (template_concept0_p1 != null || template_concept0_v1 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept0_p1 != null || template_concept0_v1 != null) && (template_concept0_p2 != null || template_concept0_v2 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept0_p2 != null || template_concept0_v2 != null) && (template_concept0_p3 != null || template_concept0_v3 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if template_concept0_last_label != null
{{{,...}}}}}}}}}{{{#!if template_concept0_params_available || template_concept0_last_label != null
{{{>}}}}}}}}}{{{#!if template_concept0_available
{{{ }}}}}}{{{#!if template_p0_available = (template_p0 != null)
{{{#!if !template_concept0_available
##======================================= template parameter 0 contents
'''{{{#569cd6,#CornFlowerBlue {{{typename}}}}}}'''}}}{{{}}}{{{#4ec9b0,#6fdbba {{{ }}}}}}
}}}{{{#!if template_v0_available = (template_v0 != null)
{{{#4ec9b0,#6fdbba {{{}}}}}}{{{}}}{{{#ffffff '''{{{ }}}'''}}}
##======================================= template parameter 0 finished
}}}{{{#!if (template_p0_available || template_v0_available) && (template_p1 != null || template_v1 != null)
##======================================= template parameter 1
{{{, }}}}}}{{{#!if template_concept1_available = (template_cpt1 != null)
'''{{{#4ec9b0,#6fdbba {{{ }}}}}}'''}}}{{{#!if template_concept1_params_available = (template_concept1_p0 != null || template_concept1_p1 != null || template_concept1_p2 != null || template_concept1_p3 != null)
{{{#!if template_concept1_params_available || template_concept1_last_label != null
{{{<}}}}}}{{{#!if template_concept1_params_available
##======================================= template parameter 1 concept's parameters (type or value)
{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept1_p0 != null || template_concept1_v0 != null) && (template_concept1_p1 != null || template_concept1_v1 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept1_p1 != null || template_concept1_v1 != null) && (template_concept1_p2 != null || template_concept1_v2 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept1_p2 != null || template_concept1_v2 != null) && (template_concept1_p3 != null || template_concept1_v3 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if template_concept1_last_label != null
{{{,...}}}}}}}}}{{{#!if template_concept1_params_available || template_concept1_last_label != null
{{{>}}}}}}}}}{{{#!if template_concept1_available
{{{ }}}}}}{{{#!if template_p1_available = (template_p1 != null)
{{{#!if !template_concept1_available
##======================================= template parameter 1 contents
'''{{{#569cd6,#CornFlowerBlue {{{typename}}}}}}'''}}}{{{}}}{{{#4ec9b0,#6fdbba {{{ }}}}}}}}}{{{#!if template_v1_available = (template_v1 != null)
##======================================= template parameter 1 finished
{{{#4ec9b0,#6fdbba {{{}}}}}}{{{}}}{{{#ffffff '''{{{ }}}'''}}}}}}{{{#!if (template_p1_available || template_v1_available) && (template_p2 != null || template_v2 != null)
##======================================= template parameter 2
{{{, }}}}}}{{{#!if template_concept2_available = (template_cpt1 != null)
'''{{{#4ec9b0,#6fdbba {{{ }}}}}}'''}}}{{{#!if template_concept2_params_available = (template_concept2_p0 != null || template_concept2_p1 != null || template_concept2_p2 != null || template_concept2_p3 != null)
{{{#!if template_concept2_params_available || template_concept2_last_label != null
{{{<}}}}}}{{{#!if template_concept2_params_available
##======================================= template parameter 2 concept's parameters (type or value)
{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept2_p0 != null || template_concept2_v0 != null) && (template_concept2_p1 != null || template_concept2_v1 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept2_p1 != null || template_concept2_v1 != null) && (template_concept2_p2 != null || template_concept2_v2 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept2_p2 != null || template_concept2_v2 != null) && (template_concept2_p3 != null || template_concept2_v3 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if template_concept2_last_label != null
{{{,...}}}}}}}}}{{{#!if template_concept2_params_available || template_concept2_last_label != null
{{{>}}}}}}}}}{{{#!if template_concept2_available
{{{ }}}}}}{{{#!if template_p2_available = (template_p2 != null)
{{{#!if !template_concept2_available
##======================================= template parameter 2 contents
'''{{{#569cd6,#CornFlowerBlue {{{typename}}}}}}'''}}}{{{}}}{{{#4ec9b0,#6fdbba {{{ }}}}}}
}}}{{{#!if template_v2_available = (template_v2 != null)
##======================================= template parameter 2 finished
{{{#4ec9b0,#6fdbba {{{}}}}}}{{{}}}{{{#ffffff '''{{{ }}}'''}}}}}}{{{#!if template_available && template_last_label != null
##======================================= template finished
{{{, }}}}}}{{{#!if template_last_label != null
{{{...}}}}}}{{{#!if template_available || template_last_label != null
{{{>}}}}}}{{{#!if do_template_linebreak = (template_lnb != null)
##======================================= template linebreak
[br]}}}{{{#!if do_template_linebreak ;nbsp
}}}
#!if head_keyword_available!=null
{{{ }}}
#!if fn_attribute!=null
##======================================= contents
[[C++/문법/특성|{{{#a8a8a8 {{{[[attr1]]}}}}}}]]
#!if fn_attribute_lnk!=null
[[C++/문법/특성#attr1|{{{#a8a8a8 {{{[[attr1]]}}}}}}]]
#!if fn_attribute_lnb!=null
[br]
#!if fn_attribute_lnb!=null ;nbsp
#!if kw1!=null
'''{{{#569cd6,#CornFlowerBlue {{{constexpr}}}}}}'''{{{#!if kw1_post!=null
{{{_kwp1}}}}}}{{{#!if kw1_post==null
{{{ }}}}}}
#!if kw2!=null
'''{{{#569cd6,#CornFlowerBlue {{{bool}}}}}}'''{{{#!if kw2post!=null
{{{&&}}}}}}{{{#!if kw2_post==null
{{{ }}}}}}
#!if cls_attribute!=null
[[C++/문법/특성|{{{#a8a8a8 {{{[[attr2]]}}}}}}]]{{{ }}}
#!if cls_attribute_lnk!=null
[[C++/문법/특성#attr2|{{{#a8a8a8 {{{[[attr2]] }}}}}}]]
#!if ns1!=null
##======================================= namespaces
'''{{{#58fafe {{{std}}}}}}'''
#!if ns2!=null
{{{::}}}'''{{{#58fafe {{{chrono}}}}}}'''
#!if ns1!=null
{{{::}}}
#!if pre1_t!=null
##======================================= types at the front
{{{#4ec9b0,#6fdbba {{{system_clock }}}}}}
#!if pre2_t!=null
{{{::}}}{{{#4ec9b0,#6fdbba {{{duration }}}}}}
#!if pre_e!=null
{{{::}}}{{{#f0f068 {{{enum }}}}}}
#!if pre_post!=null
{{{_pre}}}
#!if pre_lnb!=null
[br]
#!if do_pre_linebreak = (pre_lnb != null)
##======================================= pre-body things finished
#!if (!do_pre_linebreak && !do_template_linebreak && head_keyword_available) && (body_v != null || body_f != null || body_mv != null || body_mf != null || body_post != null)
{{{ }}}
#!if body_v!=null
##======================================= identifiers of variable and function
{{{#a8a8a8,#a1a1a1 {{{변수-식별자}}}}}}
#!if body_mv!=null
{{{#ffffff {{{mem_val}}}}}}
#!if body_f!=null
{{{#f87a7a {{{fun}}}}}}
#!if body_mf!=null
{{{#f0a962 {{{mem_fn}}}}}}
#!if body_post!=null
##======================================= argument 1
{{{\}}}
#!if body_tmpopen!=null
{{{<}}}
#!if body_bopen!=null
{{{(}}}
#!if arg1_concept!=null
'''{{{#4ec9b0,#6fdbba {{{concept1}}}}}}'''{{{#!if arg1_concept_params!=null
{{{<}}}{{{#!if arg1_concept_tparam1!=null
{{{#4ec9b0,#6fdbba {{{T1}}}}}}}}}{{{#!if arg1_concept_tparam2!=null
{{{, }}}{{{#4ec9b0,#6fdbba {{{T2}}}}}}}}}{{{#!if arg1_concept_tparam3!=null
{{{, }}}{{{#4ec9b0,#6fdbba {{{T3}}}}}}}}}{{{>}}}}}}{{{ }}}
#!if arg1_kw!=null
'''{{{#569cd6,#CornFlowerBlue {{{const }}}}}}'''
#!if arg1_t_kw!=null
'''{{{#569cd6,#CornFlowerBlue {{{int }}}}}}'''
#!if arg1_t!=null
{{{#4ec9b0,#6fdbba {{{type1}}}}}}
#!if arg1_t_post!=null
{{{&}}}
#!if arg1_param != null && (arg1_kw != null || arg1_t_kw != null || arg1_t != null)
{{{ }}}
#!if arg1_param!=null
{{{#bcdce6 {{{param1}}}}}}
#!if arg2_concept != null || arg2_kw != null || arg2_t_kw != null || arg2_t != null || arg2_param != null
{{{, }}}
#!if arg2_concept!=null
##======================================= argument 2
'''{{{#4ec9b0,#6fdbba {{{concept2}}}}}}'''{{{#!if arg2_concept_params!=null
{{{<}}}{{{#!if arg2_concept_tparam1!=null
{{{#4ec9b0,#6fdbba {{{T1}}}}}}}}}{{{#!if arg2_concept_tparam2!=null
{{{, }}}{{{#4ec9b0,#6fdbba {{{T2}}}}}}}}}{{{#!if arg2_concept_tparam3!=null
{{{, }}}{{{#4ec9b0,#6fdbba {{{T3}}}}}}}}}{{{>}}}}}}{{{ }}}
#!if arg2_kw!=null
'''{{{#569cd6,#CornFlowerBlue {{{const }}}}}}'''
#!if arg2_t_kw!=null
'''{{{#569cd6,#CornFlowerBlue {{{int }}}}}}'''
#!if arg2_t!=null
{{{#4ec9b0,#6fdbba {{{type2}}}}}}
#!if arg2_t_post!=null
{{{&}}}
#!if arg2_param!=null && (arg2_kw != null || arg2_t_kw != null || arg2_t != null)
{{{ }}}
#!if arg2_param!=null
{{{#bcdce6 {{{param2}}}}}}
#!if arg3_concept != null || arg3_kw != null || arg3_t_kw != null || arg3_t != null || arg3_param != null
{{{, }}}
#!if arg3_concept!=null
##======================================= argument 3
'''{{{#4ec9b0,#6fdbba {{{concept3}}}}}}'''{{{#!if arg3_concept_params!=null
{{{<}}}{{{#!if arg3_concept_tparam1!=null
{{{#4ec9b0,#6fdbba {{{T1}}}}}}}}}{{{#!if arg3_concept_tparam2!=null
{{{, }}}{{{#4ec9b0,#6fdbba {{{T2}}}}}}}}}{{{#!if arg3_concept_tparam3!=null
{{{, }}}{{{#4ec9b0,#6fdbba {{{T3}}}}}}}}}{{{>}}}}}}{{{ }}}
#!if arg3_kw!=null
'''{{{#569cd6,#CornFlowerBlue {{{const }}}}}}'''
#!if arg3_t_kw!=null
'''{{{#569cd6,#CornFlowerBlue {{{int }}}}}}'''
#!if arg3_t!=null
{{{#4ec9b0,#6fdbba {{{type3}}}}}}
#!if arg3_t_post!=null
{{{&}}}
#!if arg3_param!=null && (arg3_kw != null || arg3_t_kw != null || arg3_t != null)
{{{ }}}
#!if arg3_param!=null
{{{#bcdce6 {{{param3}}}}}}
#!if arg4_concept != null || arg4_kw != null || arg4_t_kw != null || arg4_t != null || arg4_param != null
{{{, }}}
#!if arg4_concept!=null
##======================================= argument4
'''{{{#4ec9b0,#6fdbba {{{concept3}}}}}}'''{{{#!if arg4_concept_params!=null
{{{<}}}{{{#!if arg4_concept_tparam1!=null
{{{#4ec9b0,#6fdbba {{{T1}}}}}}}}}{{{#!if arg4_concept_tparam2!=null
{{{, }}}{{{#4ec9b0,#6fdbba {{{T2}}}}}}}}}{{{#!if arg4_concept_tparam4!=null
{{{, }}}{{{#4ec9b0,#6fdbba {{{T3}}}}}}}}}{{{>}}}}}}{{{ }}}
#!if arg4_kw!=null
'''{{{#569cd6,#CornFlowerBlue {{{const }}}}}}'''
#!if arg4_t_kw!=null
'''{{{#569cd6,#CornFlowerBlue {{{int }}}}}}'''
#!if arg4_t!=null
{{{#4ec9b0,#6fdbba {{{type4}}}}}}
#!if arg4_t_post!=null
{{{&}}}
#!if arg4_param!=null && (arg4_kw != null || arg4_t_kw != null || arg4_t != null)
{{{ }}}
#!if arg4_param!=null
{{{#bcdce6 {{{param4}}}}}}
#!if arg5_param!=null
##======================================= argument5, argument6
{{{, }}}{{{#bcdce6 {{{param5}}}}}}
#!if arg6_param != null
{{{, }}}{{{#bcdce6 {{{param5}}}}}}
#!if arg_last_dots != null
{{{, ...}}}
#!if body_bclose!=null
##=======================================
{{{)}}}
#!if body_lnb!=null
[br]
#!if body_lnb!=null
{{{ }}}
#!if body_spec1!=null
'''{{{#DodgerBlue,#CornFlowerBlue {{{ requires}}}}}}'''
#!if body_spec1_paren != null
{{{(}}}
#!if body_spec1_ref!=null
{{{&}}}
#!if body_spec2!=null
{{{#!if body_spec1!=null && body_spec1_paren == null
{{{ }}}}}}'''{{{#DodgerBlue,#CornFlowerBlue {{{noexcept}}}}}}'''
#!if body_spec2_paren != null
{{{(}}}
#!if body_spec2_label != null
{{{...}}}
#!if body_spec2_paren != null
{{{)}}}
#!if body_spec1_paren != null
{{{)}}}
#!if body_tmpclose!=null
##======================================= last labels
{{{>}}}
#!if label_last!=null
{{{}}}
#!if last!=null
{{{;}}}
#!if head_comment!=null
{{{#57A64A,#5EBD46 }}}
#!if head_lnb!=null
##======================================= include and import
[br]
#!if import!=null
'''{{{#DodgerBlue,#CornFlowerBlue {{{import }}}}}}'''{{{#C8865E {{{<>}}}}}}{{{;}}}
#!if include!=null
##======================================= the keyword at head (can be `template`)
{{{#include <>}}}
#!if (head_keyword_available = head_keyword != null)
##======================================= template parameters begin
'''{{{#DodgerBlue,#CornFlowerBlue {{{{}}}}}}'''
#!if template_available = (template_p0 != null || template_v0 != null || template_p1 != null || template_v1 != null || template_p2 != null || template_v2 != null || template_p3 != null || template_v3 != null)
#!if template_available || template_last_label != null
##======================================= template parameter 0
##======================================= template parameter 0 concept
{{{<}}}{{{#!if template_concept0_available = (template_cpt0 != null)
'''{{{#4ec9b0,#6fdbba {{{ }}}}}}'''}}}{{{#!if template_concept0_params_available = (template_concept0_p0 != null || template_concept0_p1 != null || template_concept0_p2 != null || template_concept0_p3 != null)
{{{#!if template_concept0_params_available || template_concept0_last_label != null
{{{<}}}}}}{{{#!if template_concept0_params_available
##======================================= template parameter 0 concept's parameters (type or value)
{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept0_p0 != null || template_concept0_v0 != null) && (template_concept0_p1 != null || template_concept0_v1 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept0_p1 != null || template_concept0_v1 != null) && (template_concept0_p2 != null || template_concept0_v2 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept0_p2 != null || template_concept0_v2 != null) && (template_concept0_p3 != null || template_concept0_v3 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if template_concept0_last_label != null
{{{,...}}}}}}}}}{{{#!if template_concept0_params_available || template_concept0_last_label != null
{{{>}}}}}}}}}{{{#!if template_concept0_available
{{{ }}}}}}{{{#!if template_p0_available = (template_p0 != null)
{{{#!if !template_concept0_available
##======================================= template parameter 0 contents
'''{{{#569cd6,#CornFlowerBlue {{{typename}}}}}}'''}}}{{{}}}{{{#4ec9b0,#6fdbba {{{ }}}}}}
}}}{{{#!if template_v0_available = (template_v0 != null)
{{{#4ec9b0,#6fdbba {{{}}}}}}{{{}}}{{{#ffffff '''{{{ }}}'''}}}
##======================================= template parameter 0 finished
}}}{{{#!if (template_p0_available || template_v0_available) && (template_p1 != null || template_v1 != null)
##======================================= template parameter 1
{{{, }}}}}}{{{#!if template_concept1_available = (template_cpt1 != null)
'''{{{#4ec9b0,#6fdbba {{{ }}}}}}'''}}}{{{#!if template_concept1_params_available = (template_concept1_p0 != null || template_concept1_p1 != null || template_concept1_p2 != null || template_concept1_p3 != null)
{{{#!if template_concept1_params_available || template_concept1_last_label != null
{{{<}}}}}}{{{#!if template_concept1_params_available
##======================================= template parameter 1 concept's parameters (type or value)
{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept1_p0 != null || template_concept1_v0 != null) && (template_concept1_p1 != null || template_concept1_v1 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept1_p1 != null || template_concept1_v1 != null) && (template_concept1_p2 != null || template_concept1_v2 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept1_p2 != null || template_concept1_v2 != null) && (template_concept1_p3 != null || template_concept1_v3 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if template_concept1_last_label != null
{{{,...}}}}}}}}}{{{#!if template_concept1_params_available || template_concept1_last_label != null
{{{>}}}}}}}}}{{{#!if template_concept1_available
{{{ }}}}}}{{{#!if template_p1_available = (template_p1 != null)
{{{#!if !template_concept1_available
##======================================= template parameter 1 contents
'''{{{#569cd6,#CornFlowerBlue {{{typename}}}}}}'''}}}{{{}}}{{{#4ec9b0,#6fdbba {{{ }}}}}}}}}{{{#!if template_v1_available = (template_v1 != null)
##======================================= template parameter 1 finished
{{{#4ec9b0,#6fdbba {{{}}}}}}{{{}}}{{{#ffffff '''{{{ }}}'''}}}}}}{{{#!if (template_p1_available || template_v1_available) && (template_p2 != null || template_v2 != null)
##======================================= template parameter 2
{{{, }}}}}}{{{#!if template_concept2_available = (template_cpt1 != null)
'''{{{#4ec9b0,#6fdbba {{{ }}}}}}'''}}}{{{#!if template_concept2_params_available = (template_concept2_p0 != null || template_concept2_p1 != null || template_concept2_p2 != null || template_concept2_p3 != null)
{{{#!if template_concept2_params_available || template_concept2_last_label != null
{{{<}}}}}}{{{#!if template_concept2_params_available
##======================================= template parameter 2 concept's parameters (type or value)
{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept2_p0 != null || template_concept2_v0 != null) && (template_concept2_p1 != null || template_concept2_v1 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept2_p1 != null || template_concept2_v1 != null) && (template_concept2_p2 != null || template_concept2_v2 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept2_p2 != null || template_concept2_v2 != null) && (template_concept2_p3 != null || template_concept2_v3 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if template_concept2_last_label != null
{{{,...}}}}}}}}}{{{#!if template_concept2_params_available || template_concept2_last_label != null
{{{>}}}}}}}}}{{{#!if template_concept2_available
{{{ }}}}}}{{{#!if template_p2_available = (template_p2 != null)
{{{#!if !template_concept2_available
##======================================= template parameter 2 contents
'''{{{#569cd6,#CornFlowerBlue {{{typename}}}}}}'''}}}{{{}}}{{{#4ec9b0,#6fdbba {{{ }}}}}}
}}}{{{#!if template_v2_available = (template_v2 != null)
##======================================= template parameter 2 finished
{{{#4ec9b0,#6fdbba {{{}}}}}}{{{}}}{{{#ffffff '''{{{ }}}'''}}}}}}{{{#!if template_available && template_last_label != null
##======================================= template finished
{{{, }}}}}}{{{#!if template_last_label != null
{{{...}}}}}}{{{#!if template_available || template_last_label != null
{{{>}}}}}}{{{#!if do_template_linebreak = (template_lnb != null)
##======================================= template linebreak
[br]}}}{{{#!if do_template_linebreak ;nbsp
}}}
#!if head_keyword_available!=null
{{{ }}}
#!if fn_attribute!=null
##======================================= contents
[[C++/문법/특성|{{{#a8a8a8 {{{[[attr1]]}}}}}}]]
#!if fn_attribute_lnk!=null
[[C++/문법/특성#attr1|{{{#a8a8a8 {{{[[attr1]]}}}}}}]]
#!if fn_attribute_lnb!=null
[br]
#!if fn_attribute_lnb!=null ;nbsp
#!if kw1!=null
'''{{{#569cd6,#CornFlowerBlue {{{contexpr}}}}}}'''{{{#!if kw1_post!=null
{{{_kwp1}}}}}}{{{#!if kw1_post==null
{{{ }}}}}}
#!if kw2!=null
'''{{{#569cd6,#CornFlowerBlue {{{long long}}}}}}'''{{{#!if kw2post!=null
{{{&&}}}}}}{{{#!if kw2_post==null
{{{ }}}}}}
#!if cls_attribute!=null
[[C++/문법/특성|{{{#a8a8a8 {{{[[attr2]]}}}}}}]]{{{ }}}
#!if cls_attribute_lnk!=null
[[C++/문법/특성#attr2|{{{#a8a8a8 {{{[[attr2]] }}}}}}]]
#!if ns1!=null
##======================================= namespaces
'''{{{#58fafe {{{std}}}}}}'''
#!if ns2!=null
{{{::}}}'''{{{#58fafe {{{chrono}}}}}}'''
#!if ns1!=null
{{{::}}}
#!if pre1_t!=null
##======================================= types at the front
{{{#4ec9b0,#6fdbba {{{system_clock }}}}}}
#!if pre2_t!=null
{{{::}}}{{{#4ec9b0,#6fdbba {{{duration }}}}}}
#!if pre_e!=null
{{{::}}}{{{#f0f068 {{{enum }}}}}}
#!if pre_post!=null
{{{_pre}}}
#!if pre_lnb!=null
[br]
#!if do_pre_linebreak = (pre_lnb != null)
##======================================= pre-body things finished
#!if (!do_pre_linebreak && !do_template_linebreak && head_keyword_available) && (body_v != null || body_f != null || body_mv != null || body_mf != null || body_post != null)
{{{ }}}
#!if body_v!=null
##======================================= identifiers of variable and function
{{{#a8a8a8,#a1a1a1 {{{val}}}}}}
#!if body_mv!=null
{{{#ffffff {{{mem_val}}}}}}
#!if body_f!=null
{{{#f87a7a {{{fun}}}}}}
#!if body_mf!=null
{{{#f0a962 {{{mem_fn}}}}}}
#!if body_post!=null
##======================================= argument 1
{{{}}}
#!if body_tmpopen!=null
{{{<}}}
#!if body_bopen!=null
{{{(}}}
#!if arg1_concept!=null
'''{{{#4ec9b0,#6fdbba {{{concept1}}}}}}'''{{{#!if arg1_concept_params!=null
{{{<}}}{{{#!if arg1_concept_tparam1!=null
{{{#4ec9b0,#6fdbba {{{T1}}}}}}}}}{{{#!if arg1_concept_tparam2!=null
{{{, }}}{{{#4ec9b0,#6fdbba {{{T2}}}}}}}}}{{{#!if arg1_concept_tparam3!=null
{{{, }}}{{{#4ec9b0,#6fdbba {{{T3}}}}}}}}}{{{>}}}}}}{{{ }}}
#!if arg1_kw!=null
'''{{{#569cd6,#CornFlowerBlue {{{const }}}}}}'''
#!if arg1_t_kw!=null
'''{{{#569cd6,#CornFlowerBlue {{{int }}}}}}'''
#!if arg1_t!=null
{{{#4ec9b0,#6fdbba {{{type1}}}}}}
#!if arg1_t_post!=null
{{{&}}}
#!if arg1_param != null && (arg1_kw != null || arg1_t_kw != null || arg1_t != null)
{{{ }}}
#!if arg1_param!=null
{{{#bcdce6 {{{param1}}}}}}
#!if arg2_concept != null || arg2_kw != null || arg2_t_kw != null || arg2_t != null || arg2_param != null
{{{, }}}
#!if arg2_concept!=null
##======================================= argument 2
'''{{{#4ec9b0,#6fdbba {{{concept2}}}}}}'''{{{#!if arg2_concept_params!=null
{{{<}}}{{{#!if arg2_concept_tparam1!=null
{{{#4ec9b0,#6fdbba {{{T1}}}}}}}}}{{{#!if arg2_concept_tparam2!=null
{{{, }}}{{{#4ec9b0,#6fdbba {{{T2}}}}}}}}}{{{#!if arg2_concept_tparam3!=null
{{{, }}}{{{#4ec9b0,#6fdbba {{{T3}}}}}}}}}{{{>}}}}}}{{{ }}}
#!if arg2_kw!=null
'''{{{#569cd6,#CornFlowerBlue {{{const }}}}}}'''
#!if arg2_t_kw!=null
'''{{{#569cd6,#CornFlowerBlue {{{int }}}}}}'''
#!if arg2_t!=null
{{{#4ec9b0,#6fdbba {{{type2}}}}}}
#!if arg2_t_post!=null
{{{&}}}
#!if arg2_param!=null && (arg2_kw != null || arg2_t_kw != null || arg2_t != null)
{{{ }}}
#!if arg2_param!=null
{{{#bcdce6 {{{param2}}}}}}
#!if arg3_concept != null || arg3_kw != null || arg3_t_kw != null || arg3_t != null || arg3_param != null
{{{, }}}
#!if arg3_concept!=null
##======================================= argument 3
'''{{{#4ec9b0,#6fdbba {{{concept3}}}}}}'''{{{#!if arg3_concept_params!=null
{{{<}}}{{{#!if arg3_concept_tparam1!=null
{{{#4ec9b0,#6fdbba {{{T1}}}}}}}}}{{{#!if arg3_concept_tparam2!=null
{{{, }}}{{{#4ec9b0,#6fdbba {{{T2}}}}}}}}}{{{#!if arg3_concept_tparam3!=null
{{{, }}}{{{#4ec9b0,#6fdbba {{{T3}}}}}}}}}{{{>}}}}}}{{{ }}}
#!if arg3_kw!=null
'''{{{#569cd6,#CornFlowerBlue {{{const }}}}}}'''
#!if arg3_t_kw!=null
'''{{{#569cd6,#CornFlowerBlue {{{int }}}}}}'''
#!if arg3_t!=null
{{{#4ec9b0,#6fdbba {{{type3}}}}}}
#!if arg3_t_post!=null
{{{&}}}
#!if arg3_param!=null && (arg3_kw != null || arg3_t_kw != null || arg3_t != null)
{{{ }}}
#!if arg3_param!=null
{{{#bcdce6 {{{param3}}}}}}
#!if arg4_concept != null || arg4_kw != null || arg4_t_kw != null || arg4_t != null || arg4_param != null
{{{, }}}
#!if arg4_concept!=null
##======================================= argument4
'''{{{#4ec9b0,#6fdbba {{{concept3}}}}}}'''{{{#!if arg4_concept_params!=null
{{{<}}}{{{#!if arg4_concept_tparam1!=null
{{{#4ec9b0,#6fdbba {{{T1}}}}}}}}}{{{#!if arg4_concept_tparam2!=null
{{{, }}}{{{#4ec9b0,#6fdbba {{{T2}}}}}}}}}{{{#!if arg4_concept_tparam4!=null
{{{, }}}{{{#4ec9b0,#6fdbba {{{T3}}}}}}}}}{{{>}}}}}}{{{ }}}
#!if arg4_kw!=null
'''{{{#569cd6,#CornFlowerBlue {{{const }}}}}}'''
#!if arg4_t_kw!=null
'''{{{#569cd6,#CornFlowerBlue {{{int }}}}}}'''
#!if arg4_t!=null
{{{#4ec9b0,#6fdbba {{{type4}}}}}}
#!if arg4_t_post!=null
{{{&}}}
#!if arg4_param!=null && (arg4_kw != null || arg4_t_kw != null || arg4_t != null)
{{{ }}}
#!if arg4_param!=null
{{{#bcdce6 {{{param4}}}}}}
#!if arg5_param!=null
##======================================= argument5, argument6
{{{, }}}{{{#bcdce6 {{{param5}}}}}}
#!if arg6_param != null
{{{, }}}{{{#bcdce6 {{{param5}}}}}}
#!if arg_last_dots != null
{{{, ...}}}
#!if body_bclose!=null
##=======================================
{{{)}}}
#!if body_lnb!=null
[br]
#!if body_lnb!=null
{{{ }}}
#!if body_spec1!=null
'''{{{#DodgerBlue,#CornFlowerBlue {{{ const}}}}}}'''
#!if body_spec1_paren != null
{{{(}}}
#!if body_spec1_ref!=null
{{{&}}}
#!if body_spec2!=null
{{{#!if body_spec1!=null && body_spec1_paren == null
{{{ }}}}}}'''{{{#DodgerBlue,#CornFlowerBlue {{{noexcept}}}}}}'''
#!if body_spec2_paren != null
{{{(}}}
#!if body_spec2_label != null
{{{...}}}
#!if body_spec2_paren != null
{{{)}}}
#!if body_spec1_paren != null
{{{)}}}
#!if body_tmpclose!=null
##======================================= last labels
{{{>}}}
#!if label_last!=null
{{{}}}
#!if last!=null
{{{;}}}
#!if head_comment!=null
{{{#57A64A,#5EBD46 }}}
#!if head_lnb!=null
##======================================= include and import
[br]
#!if import!=null
'''{{{#DodgerBlue,#CornFlowerBlue {{{import }}}}}}'''{{{#C8865E {{{<>}}}}}}{{{;}}}
#!if include!=null
##======================================= the keyword at head (can be `template`)
{{{#include <>}}}
#!if (head_keyword_available = head_keyword != null)
##======================================= template parameters begin
'''{{{#DodgerBlue,#CornFlowerBlue {{{}}}}}}'''
#!if template_available = (template_p0 != null || template_v0 != null || template_p1 != null || template_v1 != null || template_p2 != null || template_v2 != null || template_p3 != null || template_v3 != null)
#!if template_available || template_last_label != null
##======================================= template parameter 0
##======================================= template parameter 0 concept
{{{<}}}{{{#!if template_concept0_available = (template_cpt0 != null)
'''{{{#4ec9b0,#6fdbba {{{ }}}}}}'''}}}{{{#!if template_concept0_params_available = (template_concept0_p0 != null || template_concept0_p1 != null || template_concept0_p2 != null || template_concept0_p3 != null)
{{{#!if template_concept0_params_available || template_concept0_last_label != null
{{{<}}}}}}{{{#!if template_concept0_params_available
##======================================= template parameter 0 concept's parameters (type or value)
{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept0_p0 != null || template_concept0_v0 != null) && (template_concept0_p1 != null || template_concept0_v1 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept0_p1 != null || template_concept0_v1 != null) && (template_concept0_p2 != null || template_concept0_v2 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept0_p2 != null || template_concept0_v2 != null) && (template_concept0_p3 != null || template_concept0_v3 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if template_concept0_last_label != null
{{{,...}}}}}}}}}{{{#!if template_concept0_params_available || template_concept0_last_label != null
{{{>}}}}}}}}}{{{#!if template_concept0_available
{{{ }}}}}}{{{#!if template_p0_available = (template_p0 != null)
{{{#!if !template_concept0_available
##======================================= template parameter 0 contents
'''{{{#569cd6,#CornFlowerBlue {{{typename}}}}}}'''}}}{{{}}}{{{#4ec9b0,#6fdbba {{{ }}}}}}
}}}{{{#!if template_v0_available = (template_v0 != null)
{{{#4ec9b0,#6fdbba {{{}}}}}}{{{}}}{{{#ffffff '''{{{ }}}'''}}}
##======================================= template parameter 0 finished
}}}{{{#!if (template_p0_available || template_v0_available) && (template_p1 != null || template_v1 != null)
##======================================= template parameter 1
{{{, }}}}}}{{{#!if template_concept1_available = (template_cpt1 != null)
'''{{{#4ec9b0,#6fdbba {{{ }}}}}}'''}}}{{{#!if template_concept1_params_available = (template_concept1_p0 != null || template_concept1_p1 != null || template_concept1_p2 != null || template_concept1_p3 != null)
{{{#!if template_concept1_params_available || template_concept1_last_label != null
{{{<}}}}}}{{{#!if template_concept1_params_available
##======================================= template parameter 1 concept's parameters (type or value)
{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept1_p0 != null || template_concept1_v0 != null) && (template_concept1_p1 != null || template_concept1_v1 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept1_p1 != null || template_concept1_v1 != null) && (template_concept1_p2 != null || template_concept1_v2 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept1_p2 != null || template_concept1_v2 != null) && (template_concept1_p3 != null || template_concept1_v3 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if template_concept1_last_label != null
{{{,...}}}}}}}}}{{{#!if template_concept1_params_available || template_concept1_last_label != null
{{{>}}}}}}}}}{{{#!if template_concept1_available
{{{ }}}}}}{{{#!if template_p1_available = (template_p1 != null)
{{{#!if !template_concept1_available
##======================================= template parameter 1 contents
'''{{{#569cd6,#CornFlowerBlue {{{typename}}}}}}'''}}}{{{}}}{{{#4ec9b0,#6fdbba {{{ }}}}}}}}}{{{#!if template_v1_available = (template_v1 != null)
##======================================= template parameter 1 finished
{{{#4ec9b0,#6fdbba {{{}}}}}}{{{}}}{{{#ffffff '''{{{ }}}'''}}}}}}{{{#!if (template_p1_available || template_v1_available) && (template_p2 != null || template_v2 != null)
##======================================= template parameter 2
{{{, }}}}}}{{{#!if template_concept2_available = (template_cpt1 != null)
'''{{{#4ec9b0,#6fdbba {{{ }}}}}}'''}}}{{{#!if template_concept2_params_available = (template_concept2_p0 != null || template_concept2_p1 != null || template_concept2_p2 != null || template_concept2_p3 != null)
{{{#!if template_concept2_params_available || template_concept2_last_label != null
{{{<}}}}}}{{{#!if template_concept2_params_available
##======================================= template parameter 2 concept's parameters (type or value)
{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept2_p0 != null || template_concept2_v0 != null) && (template_concept2_p1 != null || template_concept2_v1 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept2_p1 != null || template_concept2_v1 != null) && (template_concept2_p2 != null || template_concept2_v2 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept2_p2 != null || template_concept2_v2 != null) && (template_concept2_p3 != null || template_concept2_v3 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if template_concept2_last_label != null
{{{,...}}}}}}}}}{{{#!if template_concept2_params_available || template_concept2_last_label != null
{{{>}}}}}}}}}{{{#!if template_concept2_available
{{{ }}}}}}{{{#!if template_p2_available = (template_p2 != null)
{{{#!if !template_concept2_available
##======================================= template parameter 2 contents
'''{{{#569cd6,#CornFlowerBlue {{{typename}}}}}}'''}}}{{{}}}{{{#4ec9b0,#6fdbba {{{ }}}}}}
}}}{{{#!if template_v2_available = (template_v2 != null)
##======================================= template parameter 2 finished
{{{#4ec9b0,#6fdbba {{{}}}}}}{{{}}}{{{#ffffff '''{{{ }}}'''}}}}}}{{{#!if template_available && template_last_label != null
##======================================= template finished
{{{, }}}}}}{{{#!if template_last_label != null
{{{...}}}}}}{{{#!if template_available || template_last_label != null
{{{>}}}}}}{{{#!if do_template_linebreak = (template_lnb != null)
##======================================= template linebreak
[br]}}}{{{#!if do_template_linebreak ;nbsp
}}}
#!if head_keyword_available!=null
{{{ }}}
#!if fn_attribute!=null
##======================================= contents
[[C++/문법/특성|{{{#a8a8a8 {{{[[attr1]]}}}}}}]]
#!if fn_attribute_lnk!=null
[[C++/문법/특성#attr1|{{{#a8a8a8 {{{[[attr1]]}}}}}}]]
#!if fn_attribute_lnb!=null
[br]
#!if fn_attribute_lnb!=null ;nbsp
#!if kw1!=null
'''{{{#569cd6,#CornFlowerBlue {{{contexpr}}}}}}'''{{{#!if kw1_post!=null
{{{_kwp1}}}}}}{{{#!if kw1_post==null
{{{ }}}}}}
#!if kw2!=null
'''{{{#569cd6,#CornFlowerBlue {{{long long}}}}}}'''{{{#!if kw2post!=null
{{{&&}}}}}}{{{#!if kw2_post==null
{{{ }}}}}}
#!if cls_attribute!=null
[[C++/문법/특성|{{{#a8a8a8 {{{[[attr2]]}}}}}}]]{{{ }}}
#!if cls_attribute_lnk!=null
[[C++/문법/특성#attr2|{{{#a8a8a8 {{{[[attr2]] }}}}}}]]
#!if ns1!=null
##======================================= namespaces
'''{{{#58fafe {{{std}}}}}}'''
#!if ns2!=null
{{{::}}}'''{{{#58fafe {{{chrono}}}}}}'''
#!if ns1!=null
{{{::}}}
#!if pre1_t!=null
##======================================= types at the front
{{{#4ec9b0,#6fdbba {{{system_clock }}}}}}
#!if pre2_t!=null
{{{::}}}{{{#4ec9b0,#6fdbba {{{duration }}}}}}
#!if pre_e!=null
{{{::}}}{{{#f0f068 {{{enum }}}}}}
#!if pre_post!=null
{{{_pre}}}
#!if pre_lnb!=null
[br]
#!if do_pre_linebreak = (pre_lnb != null)
##======================================= pre-body things finished
#!if (!do_pre_linebreak && !do_template_linebreak && head_keyword_available) && (body_v != null || body_f != null || body_mv != null || body_mf != null || body_post != null)
{{{ }}}
#!if body_v!=null
##======================================= identifiers of variable and function
{{{#a8a8a8,#a1a1a1 {{{val}}}}}}
#!if body_mv!=null
{{{#ffffff {{{mem_val}}}}}}
#!if body_f!=null
{{{#f87a7a {{{fun}}}}}}
#!if body_mf!=null
{{{#f0a962 {{{mem_fn}}}}}}
#!if body_post!=null
##======================================= argument 1
{{{}}}
#!if body_tmpopen!=null
{{{<}}}
#!if body_bopen!=null
{{{(}}}
#!if arg1_concept!=null
'''{{{#4ec9b0,#6fdbba {{{concept1}}}}}}'''{{{#!if arg1_concept_params!=null
{{{<}}}{{{#!if arg1_concept_tparam1!=null
{{{#4ec9b0,#6fdbba {{{T1}}}}}}}}}{{{#!if arg1_concept_tparam2!=null
{{{, }}}{{{#4ec9b0,#6fdbba {{{T2}}}}}}}}}{{{#!if arg1_concept_tparam3!=null
{{{, }}}{{{#4ec9b0,#6fdbba {{{T3}}}}}}}}}{{{>}}}}}}{{{ }}}
#!if arg1_kw!=null
'''{{{#569cd6,#CornFlowerBlue {{{const }}}}}}'''
#!if arg1_t_kw!=null
'''{{{#569cd6,#CornFlowerBlue {{{int }}}}}}'''
#!if arg1_t!=null
{{{#4ec9b0,#6fdbba {{{type1}}}}}}
#!if arg1_t_post!=null
{{{&}}}
#!if arg1_param != null && (arg1_kw != null || arg1_t_kw != null || arg1_t != null)
{{{ }}}
#!if arg1_param!=null
{{{#bcdce6 {{{param1}}}}}}
#!if arg2_concept != null || arg2_kw != null || arg2_t_kw != null || arg2_t != null || arg2_param != null
{{{, }}}
#!if arg2_concept!=null
##======================================= argument 2
'''{{{#4ec9b0,#6fdbba {{{concept2}}}}}}'''{{{#!if arg2_concept_params!=null
{{{<}}}{{{#!if arg2_concept_tparam1!=null
{{{#4ec9b0,#6fdbba {{{T1}}}}}}}}}{{{#!if arg2_concept_tparam2!=null
{{{, }}}{{{#4ec9b0,#6fdbba {{{T2}}}}}}}}}{{{#!if arg2_concept_tparam3!=null
{{{, }}}{{{#4ec9b0,#6fdbba {{{T3}}}}}}}}}{{{>}}}}}}{{{ }}}
#!if arg2_kw!=null
'''{{{#569cd6,#CornFlowerBlue {{{const }}}}}}'''
#!if arg2_t_kw!=null
'''{{{#569cd6,#CornFlowerBlue {{{int }}}}}}'''
#!if arg2_t!=null
{{{#4ec9b0,#6fdbba {{{type2}}}}}}
#!if arg2_t_post!=null
{{{&}}}
#!if arg2_param!=null && (arg2_kw != null || arg2_t_kw != null || arg2_t != null)
{{{ }}}
#!if arg2_param!=null
{{{#bcdce6 {{{param2}}}}}}
#!if arg3_concept != null || arg3_kw != null || arg3_t_kw != null || arg3_t != null || arg3_param != null
{{{, }}}
#!if arg3_concept!=null
##======================================= argument 3
'''{{{#4ec9b0,#6fdbba {{{concept3}}}}}}'''{{{#!if arg3_concept_params!=null
{{{<}}}{{{#!if arg3_concept_tparam1!=null
{{{#4ec9b0,#6fdbba {{{T1}}}}}}}}}{{{#!if arg3_concept_tparam2!=null
{{{, }}}{{{#4ec9b0,#6fdbba {{{T2}}}}}}}}}{{{#!if arg3_concept_tparam3!=null
{{{, }}}{{{#4ec9b0,#6fdbba {{{T3}}}}}}}}}{{{>}}}}}}{{{ }}}
#!if arg3_kw!=null
'''{{{#569cd6,#CornFlowerBlue {{{const }}}}}}'''
#!if arg3_t_kw!=null
'''{{{#569cd6,#CornFlowerBlue {{{int }}}}}}'''
#!if arg3_t!=null
{{{#4ec9b0,#6fdbba {{{type3}}}}}}
#!if arg3_t_post!=null
{{{&}}}
#!if arg3_param!=null && (arg3_kw != null || arg3_t_kw != null || arg3_t != null)
{{{ }}}
#!if arg3_param!=null
{{{#bcdce6 {{{param3}}}}}}
#!if arg4_concept != null || arg4_kw != null || arg4_t_kw != null || arg4_t != null || arg4_param != null
{{{, }}}
#!if arg4_concept!=null
##======================================= argument4
'''{{{#4ec9b0,#6fdbba {{{concept3}}}}}}'''{{{#!if arg4_concept_params!=null
{{{<}}}{{{#!if arg4_concept_tparam1!=null
{{{#4ec9b0,#6fdbba {{{T1}}}}}}}}}{{{#!if arg4_concept_tparam2!=null
{{{, }}}{{{#4ec9b0,#6fdbba {{{T2}}}}}}}}}{{{#!if arg4_concept_tparam4!=null
{{{, }}}{{{#4ec9b0,#6fdbba {{{T3}}}}}}}}}{{{>}}}}}}{{{ }}}
#!if arg4_kw!=null
'''{{{#569cd6,#CornFlowerBlue {{{const }}}}}}'''
#!if arg4_t_kw!=null
'''{{{#569cd6,#CornFlowerBlue {{{int }}}}}}'''
#!if arg4_t!=null
{{{#4ec9b0,#6fdbba {{{type4}}}}}}
#!if arg4_t_post!=null
{{{&}}}
#!if arg4_param!=null && (arg4_kw != null || arg4_t_kw != null || arg4_t != null)
{{{ }}}
#!if arg4_param!=null
{{{#bcdce6 {{{param4}}}}}}
#!if arg5_param!=null
##======================================= argument5, argument6
{{{, }}}{{{#bcdce6 {{{param5}}}}}}
#!if arg6_param != null
{{{, }}}{{{#bcdce6 {{{param5}}}}}}
#!if arg_last_dots != null
{{{, ...}}}
#!if body_bclose!=null
##=======================================
{{{)}}}
#!if body_lnb!=null
[br]
#!if body_lnb!=null
{{{ }}}
#!if body_spec1!=null
'''{{{#DodgerBlue,#CornFlowerBlue {{{ const}}}}}}'''
#!if body_spec1_paren != null
{{{(}}}
#!if body_spec1_ref!=null
{{{&}}}
#!if body_spec2!=null
{{{#!if body_spec1!=null && body_spec1_paren == null
{{{ }}}}}}'''{{{#DodgerBlue,#CornFlowerBlue {{{noexcept}}}}}}'''
#!if body_spec2_paren != null
{{{(}}}
#!if body_spec2_label != null
{{{...}}}
#!if body_spec2_paren != null
{{{)}}}
#!if body_spec1_paren != null
{{{)}}}
#!if body_tmpclose!=null
##======================================= last labels
{{{>}}}
#!if label_last!=null
{{{...}}}
#!if last!=null
{{{;}}}
#!if head_comment!=null
{{{#57A64A,#5EBD46 }}}
#!if head_lnb!=null
##======================================= include and import
[br]
#!if import!=null
'''{{{#DodgerBlue,#CornFlowerBlue {{{import }}}}}}'''{{{#C8865E {{{<>}}}}}}{{{;}}}
#!if include!=null
##======================================= the keyword at head (can be `template`)
{{{#include <>}}}
#!if (head_keyword_available = head_keyword != null)
##======================================= template parameters begin
'''{{{#DodgerBlue,#CornFlowerBlue {{{}}}}}}}'''
#!if template_available = (template_p0 != null || template_v0 != null || template_p1 != null || template_v1 != null || template_p2 != null || template_v2 != null || template_p3 != null || template_v3 != null)
#!if template_available || template_last_label != null
##======================================= template parameter 0
##======================================= template parameter 0 concept
{{{<}}}{{{#!if template_concept0_available = (template_cpt0 != null)
'''{{{#4ec9b0,#6fdbba {{{ }}}}}}'''}}}{{{#!if template_concept0_params_available = (template_concept0_p0 != null || template_concept0_p1 != null || template_concept0_p2 != null || template_concept0_p3 != null)
{{{#!if template_concept0_params_available || template_concept0_last_label != null
{{{<}}}}}}{{{#!if template_concept0_params_available
##======================================= template parameter 0 concept's parameters (type or value)
{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept0_p0 != null || template_concept0_v0 != null) && (template_concept0_p1 != null || template_concept0_v1 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept0_p1 != null || template_concept0_v1 != null) && (template_concept0_p2 != null || template_concept0_v2 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept0_p2 != null || template_concept0_v2 != null) && (template_concept0_p3 != null || template_concept0_v3 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if template_concept0_last_label != null
{{{,...}}}}}}}}}{{{#!if template_concept0_params_available || template_concept0_last_label != null
{{{>}}}}}}}}}{{{#!if template_concept0_available
{{{ }}}}}}{{{#!if template_p0_available = (template_p0 != null)
{{{#!if !template_concept0_available
##======================================= template parameter 0 contents
'''{{{#569cd6,#CornFlowerBlue {{{typename}}}}}}'''}}}{{{}}}{{{#4ec9b0,#6fdbba {{{ }}}}}}
}}}{{{#!if template_v0_available = (template_v0 != null)
{{{#4ec9b0,#6fdbba {{{}}}}}}{{{}}}{{{#ffffff '''{{{ }}}'''}}}
##======================================= template parameter 0 finished
}}}{{{#!if (template_p0_available || template_v0_available) && (template_p1 != null || template_v1 != null)
##======================================= template parameter 1
{{{, }}}}}}{{{#!if template_concept1_available = (template_cpt1 != null)
'''{{{#4ec9b0,#6fdbba {{{ }}}}}}'''}}}{{{#!if template_concept1_params_available = (template_concept1_p0 != null || template_concept1_p1 != null || template_concept1_p2 != null || template_concept1_p3 != null)
{{{#!if template_concept1_params_available || template_concept1_last_label != null
{{{<}}}}}}{{{#!if template_concept1_params_available
##======================================= template parameter 1 concept's parameters (type or value)
{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept1_p0 != null || template_concept1_v0 != null) && (template_concept1_p1 != null || template_concept1_v1 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept1_p1 != null || template_concept1_v1 != null) && (template_concept1_p2 != null || template_concept1_v2 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept1_p2 != null || template_concept1_v2 != null) && (template_concept1_p3 != null || template_concept1_v3 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if template_concept1_last_label != null
{{{,...}}}}}}}}}{{{#!if template_concept1_params_available || template_concept1_last_label != null
{{{>}}}}}}}}}{{{#!if template_concept1_available
{{{ }}}}}}{{{#!if template_p1_available = (template_p1 != null)
{{{#!if !template_concept1_available
##======================================= template parameter 1 contents
'''{{{#569cd6,#CornFlowerBlue {{{typename}}}}}}'''}}}{{{}}}{{{#4ec9b0,#6fdbba {{{ }}}}}}}}}{{{#!if template_v1_available = (template_v1 != null)
##======================================= template parameter 1 finished
{{{#4ec9b0,#6fdbba {{{}}}}}}{{{}}}{{{#ffffff '''{{{ }}}'''}}}}}}{{{#!if (template_p1_available || template_v1_available) && (template_p2 != null || template_v2 != null)
##======================================= template parameter 2
{{{, }}}}}}{{{#!if template_concept2_available = (template_cpt1 != null)
'''{{{#4ec9b0,#6fdbba {{{ }}}}}}'''}}}{{{#!if template_concept2_params_available = (template_concept2_p0 != null || template_concept2_p1 != null || template_concept2_p2 != null || template_concept2_p3 != null)
{{{#!if template_concept2_params_available || template_concept2_last_label != null
{{{<}}}}}}{{{#!if template_concept2_params_available
##======================================= template parameter 2 concept's parameters (type or value)
{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept2_p0 != null || template_concept2_v0 != null) && (template_concept2_p1 != null || template_concept2_v1 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept2_p1 != null || template_concept2_v1 != null) && (template_concept2_p2 != null || template_concept2_v2 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept2_p2 != null || template_concept2_v2 != null) && (template_concept2_p3 != null || template_concept2_v3 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if template_concept2_last_label != null
{{{,...}}}}}}}}}{{{#!if template_concept2_params_available || template_concept2_last_label != null
{{{>}}}}}}}}}{{{#!if template_concept2_available
{{{ }}}}}}{{{#!if template_p2_available = (template_p2 != null)
{{{#!if !template_concept2_available
##======================================= template parameter 2 contents
'''{{{#569cd6,#CornFlowerBlue {{{typename}}}}}}'''}}}{{{}}}{{{#4ec9b0,#6fdbba {{{ }}}}}}
}}}{{{#!if template_v2_available = (template_v2 != null)
##======================================= template parameter 2 finished
{{{#4ec9b0,#6fdbba {{{}}}}}}{{{}}}{{{#ffffff '''{{{ }}}'''}}}}}}{{{#!if template_available && template_last_label != null
##======================================= template finished
{{{, }}}}}}{{{#!if template_last_label != null
{{{...}}}}}}{{{#!if template_available || template_last_label != null
{{{>}}}}}}{{{#!if do_template_linebreak = (template_lnb != null)
##======================================= template linebreak
[br]}}}{{{#!if do_template_linebreak ;nbsp
}}}
#!if head_keyword_available!=null
{{{ }}}
#!if fn_attribute!=null
##======================================= contents
[[C++/문법/특성|{{{#a8a8a8 {{{[[attr1]]}}}}}}]]
#!if fn_attribute_lnk!=null
[[C++/문법/특성#attr1|{{{#a8a8a8 {{{[[attr1]]}}}}}}]]
#!if fn_attribute_lnb!=null
[br]
#!if fn_attribute_lnb!=null ;nbsp
#!if kw1!=null
'''{{{#569cd6,#CornFlowerBlue {{{contexpr}}}}}}'''{{{#!if kw1_post!=null
{{{_kwp1}}}}}}{{{#!if kw1_post==null
{{{ }}}}}}
#!if kw2!=null
'''{{{#569cd6,#CornFlowerBlue {{{long long}}}}}}'''{{{#!if kw2post!=null
{{{&&}}}}}}{{{#!if kw2_post==null
{{{ }}}}}}
#!if cls_attribute!=null
[[C++/문법/특성|{{{#a8a8a8 {{{[[attr2]]}}}}}}]]{{{ }}}
#!if cls_attribute_lnk!=null
[[C++/문법/특성#attr2|{{{#a8a8a8 {{{[[attr2]] }}}}}}]]
#!if ns1!=null
##======================================= namespaces
'''{{{#58fafe {{{std}}}}}}'''
#!if ns2!=null
{{{::}}}'''{{{#58fafe {{{chrono}}}}}}'''
#!if ns1!=null
{{{::}}}
#!if pre1_t!=null
##======================================= types at the front
{{{#4ec9b0,#6fdbba {{{system_clock }}}}}}
#!if pre2_t!=null
{{{::}}}{{{#4ec9b0,#6fdbba {{{duration }}}}}}
#!if pre_e!=null
{{{::}}}{{{#f0f068 {{{enum }}}}}}
#!if pre_post!=null
{{{_pre}}}
#!if pre_lnb!=null
[br]
#!if do_pre_linebreak = (pre_lnb != null)
##======================================= pre-body things finished
#!if (!do_pre_linebreak && !do_template_linebreak && head_keyword_available) && (body_v != null || body_f != null || body_mv != null || body_mf != null || body_post != null)
{{{ }}}
#!if body_v!=null
##======================================= identifiers of variable and function
{{{#a8a8a8,#a1a1a1 {{{val}}}}}}
#!if body_mv!=null
{{{#ffffff {{{mem_val}}}}}}
#!if body_f!=null
{{{#f87a7a {{{fun}}}}}}
#!if body_mf!=null
{{{#f0a962 {{{mem_fn}}}}}}
#!if body_post!=null
##======================================= argument 1
{{{}}}
#!if body_tmpopen!=null
{{{<}}}
#!if body_bopen!=null
{{{(}}}
#!if arg1_concept!=null
'''{{{#4ec9b0,#6fdbba {{{concept1}}}}}}'''{{{#!if arg1_concept_params!=null
{{{<}}}{{{#!if arg1_concept_tparam1!=null
{{{#4ec9b0,#6fdbba {{{T1}}}}}}}}}{{{#!if arg1_concept_tparam2!=null
{{{, }}}{{{#4ec9b0,#6fdbba {{{T2}}}}}}}}}{{{#!if arg1_concept_tparam3!=null
{{{, }}}{{{#4ec9b0,#6fdbba {{{T3}}}}}}}}}{{{>}}}}}}{{{ }}}
#!if arg1_kw!=null
'''{{{#569cd6,#CornFlowerBlue {{{const }}}}}}'''
#!if arg1_t_kw!=null
'''{{{#569cd6,#CornFlowerBlue {{{int }}}}}}'''
#!if arg1_t!=null
{{{#4ec9b0,#6fdbba {{{type1}}}}}}
#!if arg1_t_post!=null
{{{&}}}
#!if arg1_param != null && (arg1_kw != null || arg1_t_kw != null || arg1_t != null)
{{{ }}}
#!if arg1_param!=null
{{{#bcdce6 {{{param1}}}}}}
#!if arg2_concept != null || arg2_kw != null || arg2_t_kw != null || arg2_t != null || arg2_param != null
{{{, }}}
#!if arg2_concept!=null
##======================================= argument 2
'''{{{#4ec9b0,#6fdbba {{{concept2}}}}}}'''{{{#!if arg2_concept_params!=null
{{{<}}}{{{#!if arg2_concept_tparam1!=null
{{{#4ec9b0,#6fdbba {{{T1}}}}}}}}}{{{#!if arg2_concept_tparam2!=null
{{{, }}}{{{#4ec9b0,#6fdbba {{{T2}}}}}}}}}{{{#!if arg2_concept_tparam3!=null
{{{, }}}{{{#4ec9b0,#6fdbba {{{T3}}}}}}}}}{{{>}}}}}}{{{ }}}
#!if arg2_kw!=null
'''{{{#569cd6,#CornFlowerBlue {{{const }}}}}}'''
#!if arg2_t_kw!=null
'''{{{#569cd6,#CornFlowerBlue {{{int }}}}}}'''
#!if arg2_t!=null
{{{#4ec9b0,#6fdbba {{{type2}}}}}}
#!if arg2_t_post!=null
{{{&}}}
#!if arg2_param!=null && (arg2_kw != null || arg2_t_kw != null || arg2_t != null)
{{{ }}}
#!if arg2_param!=null
{{{#bcdce6 {{{param2}}}}}}
#!if arg3_concept != null || arg3_kw != null || arg3_t_kw != null || arg3_t != null || arg3_param != null
{{{, }}}
#!if arg3_concept!=null
##======================================= argument 3
'''{{{#4ec9b0,#6fdbba {{{concept3}}}}}}'''{{{#!if arg3_concept_params!=null
{{{<}}}{{{#!if arg3_concept_tparam1!=null
{{{#4ec9b0,#6fdbba {{{T1}}}}}}}}}{{{#!if arg3_concept_tparam2!=null
{{{, }}}{{{#4ec9b0,#6fdbba {{{T2}}}}}}}}}{{{#!if arg3_concept_tparam3!=null
{{{, }}}{{{#4ec9b0,#6fdbba {{{T3}}}}}}}}}{{{>}}}}}}{{{ }}}
#!if arg3_kw!=null
'''{{{#569cd6,#CornFlowerBlue {{{const }}}}}}'''
#!if arg3_t_kw!=null
'''{{{#569cd6,#CornFlowerBlue {{{int }}}}}}'''
#!if arg3_t!=null
{{{#4ec9b0,#6fdbba {{{type3}}}}}}
#!if arg3_t_post!=null
{{{&}}}
#!if arg3_param!=null && (arg3_kw != null || arg3_t_kw != null || arg3_t != null)
{{{ }}}
#!if arg3_param!=null
{{{#bcdce6 {{{param3}}}}}}
#!if arg4_concept != null || arg4_kw != null || arg4_t_kw != null || arg4_t != null || arg4_param != null
{{{, }}}
#!if arg4_concept!=null
##======================================= argument4
'''{{{#4ec9b0,#6fdbba {{{concept3}}}}}}'''{{{#!if arg4_concept_params!=null
{{{<}}}{{{#!if arg4_concept_tparam1!=null
{{{#4ec9b0,#6fdbba {{{T1}}}}}}}}}{{{#!if arg4_concept_tparam2!=null
{{{, }}}{{{#4ec9b0,#6fdbba {{{T2}}}}}}}}}{{{#!if arg4_concept_tparam4!=null
{{{, }}}{{{#4ec9b0,#6fdbba {{{T3}}}}}}}}}{{{>}}}}}}{{{ }}}
#!if arg4_kw!=null
'''{{{#569cd6,#CornFlowerBlue {{{const }}}}}}'''
#!if arg4_t_kw!=null
'''{{{#569cd6,#CornFlowerBlue {{{int }}}}}}'''
#!if arg4_t!=null
{{{#4ec9b0,#6fdbba {{{type4}}}}}}
#!if arg4_t_post!=null
{{{&}}}
#!if arg4_param!=null && (arg4_kw != null || arg4_t_kw != null || arg4_t != null)
{{{ }}}
#!if arg4_param!=null
{{{#bcdce6 {{{param4}}}}}}
#!if arg5_param!=null
##======================================= argument5, argument6
{{{, }}}{{{#bcdce6 {{{param5}}}}}}
#!if arg6_param != null
{{{, }}}{{{#bcdce6 {{{param5}}}}}}
#!if arg_last_dots != null
{{{, ...}}}
#!if body_bclose!=null
##=======================================
{{{)}}}
#!if body_lnb!=null
[br]
#!if body_lnb!=null
{{{ }}}
#!if body_spec1!=null
'''{{{#DodgerBlue,#CornFlowerBlue {{{ const}}}}}}'''
#!if body_spec1_paren != null
{{{(}}}
#!if body_spec1_ref!=null
{{{&}}}
#!if body_spec2!=null
{{{#!if body_spec1!=null && body_spec1_paren == null
{{{ }}}}}}'''{{{#DodgerBlue,#CornFlowerBlue {{{noexcept}}}}}}'''
#!if body_spec2_paren != null
{{{(}}}
#!if body_spec2_label != null
{{{...}}}
#!if body_spec2_paren != null
{{{)}}}
#!if body_spec1_paren != null
{{{)}}}
#!if body_tmpclose!=null
##======================================= last labels
{{{>}}}
#!if label_last!=null
{{{}}}
#!if last!=null
{{{;}}}
requires 표현식 (Requires Expression) C++20 C++20
에선 개념 뿐만 아니라 매우 복잡한 전제조건을 달 수 있는 기능도 생겼다. requires
표현식은 개념과 마찬가지로 prvalue bool
을 반환하는 consteval
상수 표현식 으로서 컴파일 시점에 평가되며 런타임엔 성능이 영향이 없도록 최적화된다. 또한 마치 클래스를 정의하듯이 세부적인 사양을 정해줄 수 있다. <rowcolor=#d7d7d7,#a1a1a1>이름 설명 표준 <rowcolor=#090912,#bebebf>간단 형식 줄 마다 표현식을 기입하면 그 표현식을 실행할 수 있는지 검사한다. # <rowcolor=#090912,#bebebf>자료형 형식 typename
과 자료형 식별자를 기입하면 그 자료형이 존재하는지 검사한다.# <rowcolor=#090912,#bebebf>합성 형식 중괄호({})안에 표현식, 그리고 noexcept
와 표현식을 검사하는 개념을 선택적으로 기입할 수 있는데, 그 표현식을 실행할 수 있으며 조건을 만족하는 검사한다. # <rowcolor=#090912,#bebebf>중첩 형식 또다른 requires
식을 기입하면 그 제약조건을 만족하는지 검사한다. #
requires
식에는 네가지 유형이 있다. 각각의 유형은 사용자가 원하면 아주 복잡하게 작성할 수 있으며 제한도 딱히 없다. 심지어 상수 표현식이 아니라도 검사할 수 있기 때문에 자료형의 속성을 확인하는 데에는 한계가 완전히 사라졌다고 말할 수 있다.국소 매개변수 (Local Parameters) requires
식은 앞서 소개한 개념과는 다르게 자기만의 일종의 함수 매개변수를 가질 수 있다. 개념이 constexpr 상수 라면, requires
식은 consteval 함수 의 어법적 확장이다. 그러나 이 매개변수는 사용자가 쓰려고 정의하는 게 아니라, 컴파일 과정에서 알아서 정의되고 컴파일이 끝나면 아무 흔적도 없이 사라진다. 그러므로 코드 구현이 있는 온전한 자료형만 매개변수로 사용할 수 있다.#!syntax cpp
template<typename T>
concept Copyables = std::is_copy_constructible_v<T> && std::is_copy_assignable_v<T> && requires(T& value)
{
{ T(value) } -> std::same_as<T>;
};
template<typename T>
constexpr bool IsCopyable = Copyables<T>;
상기 코드는 표준의 메타 함수로 어떤 자료형이 복사가능한지 여부를 판별하는 개념을 보여주고 있다. 대부분의 경우 복사 생성이 가능한지, 복사 대입이 가능한지로 충분하지만, 만약 ()
연산자로 다른 값을 반환하는 경우 문제가 생길 수 있다. 이때 ()
에 lvalue
T
를 전달해서 T
를 반환하는지 확인하면 적어도 그 자료형에 대해서는 복사했을 때 반환값이 잘 들어오리라 예상할 수 있다. 참고로 국소 매개변수에 auto
를 사용하지 못한다. 연산을 하려는 자료형을 특정하고 자료형의 식별자를 확인할 수 있어야 하기 때문이다.5. 개념과 requires#!syntax cpp
template<typename T>
concept DefaultConstructibles = requires
{
T();
T{};
// T()가 생성자라면 반환되는 것은 T다.
// T()가 어떤 정적 함수라면 반환되는 것은 그 함수의 반환 자료형이다.
{ T() } -> std::same_as<T>;
{ T{} } -> std::same_as<T>;
};
requires
식은 개념을 정의할 때 사용할 수 있다. 상기 코드는 간단 형식, 합성 형식으로 기본 초기화 가 가능한지 검사하는 개념이다. 컴파일러에 비하면 정확한 구현은 아니지만, 어떤 자료형은 기본 초기화 비슷한 행위가 가능하겠구나 라는 걸 확인할 수 있다. 먼저 간단 형식으로 ()
와 {}
연산이 실행 가능한지 확인한다. 실행이 불가능하면 이 개념은 성립하지 않는다. 다음은 합성 형식으로 ()
와 {}
연산의 결과 값으로 T
가 반환되는지 확인한다. 합성 형식은 중괄호 안에 표현식을 넣고, ->
뒤에는 또다른 개념을 넣을 수 있다. 이때 쓰인 개념의 첫번째 템플릿 매개변수에는 합성 형식의 반환 자료형이 대입된다. 가령 예제에 쓰인 개념 std::same_as<T, U>
의 첫번째 매개변수 `T`
에는 합성 형식안의 표현식 T()
를 실행한 값의 자료형이 들어간다. 표준 라이브러리 의 std::same_as<T, U>
다. 이 개념은 두 자료형이 같은지 여부를 반환한다. #!syntax cpp
template<typename T>
concept DefaultConstructibles = std::is_default_constructible_v<T> && requires
{
T();
T{};
{ T() } -> std::same_as<T>;
{ T{} } -> std::same_as<T>;
};
requires
은 조건문처럼 &&
나 ||
로 이어서 검사할 수 있다. 상기 코드는 표준 라이브러리의 메타 함수 std::is_default_constructible_v<T> 를 선제 조건으로 썼다.#!syntax cpp
template<typename T>
concept DefaultConstructibles = std::is_default_constructible_v<T> && requires
{
{ T() } -> std::same_as<T>;
{ T{} } -> std::same_as<T>;
};
그런데 이 개념에서 위에 두줄은 필요없다. 왜냐하면 밑에 합성 형식문에서 실행 여부까지 같이 검사하기 때문이다. 한편 이 예제에서 std::is_default_constructible_v
로 검사하는데 왜 또 requires
식이 필요한지 궁금할 수 있다. 왜냐하면 연산자 오버로딩 때문에 다른 실행 결과가 나오기도 하기 때문이다. 예를 들어서 람다 표현식 의 사례가 있다.#!syntax cpp
template<typename T>
concept NothrowDefaultConstructibles = DefaultConstructibles<T> && requires
{
{ T() } noexcept;
{ T{} } noexcept;
};
개념을 정의할 때 또다른 개념을 쓸 수 있다. 상기 코드는 합성 형식으로 예외 사양을 검사하는 예제를 보여주고 있다.6. 템플릿과 requires [include(틀:C++ 요소, head_keyword=template,
template_p0=템플릿-매개변수1, template_p1=템플릿-매개변수2,
template_last_label=...)]
#!if head_comment!=null
{{{#57A64A,#5EBD46 }}}
#!if head_lnb!=null
##======================================= include and import
[br]
#!if import!=null
'''{{{#DodgerBlue,#CornFlowerBlue {{{import }}}}}}'''{{{#C8865E {{{<>}}}}}}{{{;}}}
#!if include!=null
##======================================= the keyword at head (can be `template`)
{{{#include <>}}}
#!if (head_keyword_available = head_keyword != null)
##======================================= template parameters begin
'''{{{#DodgerBlue,#CornFlowerBlue {{{requires}}}}}}'''
#!if template_available = (template_p0 != null || template_v0 != null || template_p1 != null || template_v1 != null || template_p2 != null || template_v2 != null || template_p3 != null || template_v3 != null)
#!if template_available || template_last_label != null
##======================================= template parameter 0
##======================================= template parameter 0 concept
{{{<}}}{{{#!if template_concept0_available = (template_cpt0 != null)
'''{{{#4ec9b0,#6fdbba {{{ }}}}}}'''}}}{{{#!if template_concept0_params_available = (template_concept0_p0 != null || template_concept0_p1 != null || template_concept0_p2 != null || template_concept0_p3 != null)
{{{#!if template_concept0_params_available || template_concept0_last_label != null
{{{<}}}}}}{{{#!if template_concept0_params_available
##======================================= template parameter 0 concept's parameters (type or value)
{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept0_p0 != null || template_concept0_v0 != null) && (template_concept0_p1 != null || template_concept0_v1 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept0_p1 != null || template_concept0_v1 != null) && (template_concept0_p2 != null || template_concept0_v2 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept0_p2 != null || template_concept0_v2 != null) && (template_concept0_p3 != null || template_concept0_v3 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if template_concept0_last_label != null
{{{,...}}}}}}}}}{{{#!if template_concept0_params_available || template_concept0_last_label != null
{{{>}}}}}}}}}{{{#!if template_concept0_available
{{{ }}}}}}{{{#!if template_p0_available = (template_p0 != null)
{{{#!if !template_concept0_available
##======================================= template parameter 0 contents
'''{{{#569cd6,#CornFlowerBlue {{{typename}}}}}}'''}}}{{{}}}{{{#4ec9b0,#6fdbba {{{ }}}}}}
}}}{{{#!if template_v0_available = (template_v0 != null)
{{{#4ec9b0,#6fdbba {{{}}}}}}{{{}}}{{{#ffffff '''{{{ }}}'''}}}
##======================================= template parameter 0 finished
}}}{{{#!if (template_p0_available || template_v0_available) && (template_p1 != null || template_v1 != null)
##======================================= template parameter 1
{{{, }}}}}}{{{#!if template_concept1_available = (template_cpt1 != null)
'''{{{#4ec9b0,#6fdbba {{{ }}}}}}'''}}}{{{#!if template_concept1_params_available = (template_concept1_p0 != null || template_concept1_p1 != null || template_concept1_p2 != null || template_concept1_p3 != null)
{{{#!if template_concept1_params_available || template_concept1_last_label != null
{{{<}}}}}}{{{#!if template_concept1_params_available
##======================================= template parameter 1 concept's parameters (type or value)
{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept1_p0 != null || template_concept1_v0 != null) && (template_concept1_p1 != null || template_concept1_v1 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept1_p1 != null || template_concept1_v1 != null) && (template_concept1_p2 != null || template_concept1_v2 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept1_p2 != null || template_concept1_v2 != null) && (template_concept1_p3 != null || template_concept1_v3 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if template_concept1_last_label != null
{{{,...}}}}}}}}}{{{#!if template_concept1_params_available || template_concept1_last_label != null
{{{>}}}}}}}}}{{{#!if template_concept1_available
{{{ }}}}}}{{{#!if template_p1_available = (template_p1 != null)
{{{#!if !template_concept1_available
##======================================= template parameter 1 contents
'''{{{#569cd6,#CornFlowerBlue {{{typename}}}}}}'''}}}{{{}}}{{{#4ec9b0,#6fdbba {{{ }}}}}}}}}{{{#!if template_v1_available = (template_v1 != null)
##======================================= template parameter 1 finished
{{{#4ec9b0,#6fdbba {{{}}}}}}{{{}}}{{{#ffffff '''{{{ }}}'''}}}}}}{{{#!if (template_p1_available || template_v1_available) && (template_p2 != null || template_v2 != null)
##======================================= template parameter 2
{{{, }}}}}}{{{#!if template_concept2_available = (template_cpt1 != null)
'''{{{#4ec9b0,#6fdbba {{{ }}}}}}'''}}}{{{#!if template_concept2_params_available = (template_concept2_p0 != null || template_concept2_p1 != null || template_concept2_p2 != null || template_concept2_p3 != null)
{{{#!if template_concept2_params_available || template_concept2_last_label != null
{{{<}}}}}}{{{#!if template_concept2_params_available
##======================================= template parameter 2 concept's parameters (type or value)
{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept2_p0 != null || template_concept2_v0 != null) && (template_concept2_p1 != null || template_concept2_v1 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept2_p1 != null || template_concept2_v1 != null) && (template_concept2_p2 != null || template_concept2_v2 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept2_p2 != null || template_concept2_v2 != null) && (template_concept2_p3 != null || template_concept2_v3 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if template_concept2_last_label != null
{{{,...}}}}}}}}}{{{#!if template_concept2_params_available || template_concept2_last_label != null
{{{>}}}}}}}}}{{{#!if template_concept2_available
{{{ }}}}}}{{{#!if template_p2_available = (template_p2 != null)
{{{#!if !template_concept2_available
##======================================= template parameter 2 contents
'''{{{#569cd6,#CornFlowerBlue {{{typename}}}}}}'''}}}{{{}}}{{{#4ec9b0,#6fdbba {{{ }}}}}}
}}}{{{#!if template_v2_available = (template_v2 != null)
##======================================= template parameter 2 finished
{{{#4ec9b0,#6fdbba {{{}}}}}}{{{}}}{{{#ffffff '''{{{ }}}'''}}}}}}{{{#!if template_available && template_last_label != null
##======================================= template finished
{{{, }}}}}}{{{#!if template_last_label != null
{{{...}}}}}}{{{#!if template_available || template_last_label != null
{{{>}}}}}}{{{#!if do_template_linebreak = (template_lnb != null)
##======================================= template linebreak
[br]}}}{{{#!if do_template_linebreak ;nbsp
}}}
#!if head_keyword_available!=null
{{{ }}}
#!if fn_attribute!=null
##======================================= contents
[[C++/문법/특성|{{{#a8a8a8 {{{[[attr1]]}}}}}}]]
#!if fn_attribute_lnk!=null
[[C++/문법/특성#attr1|{{{#a8a8a8 {{{[[attr1]]}}}}}}]]
#!if fn_attribute_lnb!=null
[br]
#!if fn_attribute_lnb!=null ;nbsp
#!if kw1!=null
'''{{{#569cd6,#CornFlowerBlue {{{contexpr}}}}}}'''{{{#!if kw1_post!=null
{{{_kwp1}}}}}}{{{#!if kw1_post==null
{{{ }}}}}}
#!if kw2!=null
'''{{{#569cd6,#CornFlowerBlue {{{long long}}}}}}'''{{{#!if kw2post!=null
{{{&&}}}}}}{{{#!if kw2_post==null
{{{ }}}}}}
#!if cls_attribute!=null
[[C++/문법/특성|{{{#a8a8a8 {{{[[attr2]]}}}}}}]]{{{ }}}
#!if cls_attribute_lnk!=null
[[C++/문법/특성#attr2|{{{#a8a8a8 {{{[[attr2]] }}}}}}]]
#!if ns1!=null
##======================================= namespaces
'''{{{#58fafe {{{std}}}}}}'''
#!if ns2!=null
{{{::}}}'''{{{#58fafe {{{chrono}}}}}}'''
#!if ns1!=null
{{{::}}}
#!if pre1_t!=null
##======================================= types at the front
{{{#4ec9b0,#6fdbba {{{system_clock }}}}}}
#!if pre2_t!=null
{{{::}}}{{{#4ec9b0,#6fdbba {{{duration }}}}}}
#!if pre_e!=null
{{{::}}}{{{#f0f068 {{{enum }}}}}}
#!if pre_post!=null
{{{_pre}}}
#!if pre_lnb!=null
[br]
#!if do_pre_linebreak = (pre_lnb != null)
##======================================= pre-body things finished
#!if (!do_pre_linebreak && !do_template_linebreak && head_keyword_available) && (body_v != null || body_f != null || body_mv != null || body_mf != null || body_post != null)
{{{ }}}
#!if body_v!=null
##======================================= identifiers of variable and function
{{{#a8a8a8,#a1a1a1 {{{val}}}}}}
#!if body_mv!=null
{{{#ffffff {{{mem_val}}}}}}
#!if body_f!=null
{{{#f87a7a {{{fun}}}}}}
#!if body_mf!=null
{{{#f0a962 {{{mem_fn}}}}}}
#!if body_post!=null
##======================================= argument 1
{{{}}}
#!if body_tmpopen!=null
{{{<}}}
#!if body_bopen!=null
{{{(}}}
#!if arg1_concept!=null
'''{{{#4ec9b0,#6fdbba {{{concept1}}}}}}'''{{{#!if arg1_concept_params!=null
{{{<}}}{{{#!if arg1_concept_tparam1!=null
{{{#4ec9b0,#6fdbba {{{T1}}}}}}}}}{{{#!if arg1_concept_tparam2!=null
{{{, }}}{{{#4ec9b0,#6fdbba {{{T2}}}}}}}}}{{{#!if arg1_concept_tparam3!=null
{{{, }}}{{{#4ec9b0,#6fdbba {{{T3}}}}}}}}}{{{>}}}}}}{{{ }}}
#!if arg1_kw!=null
'''{{{#569cd6,#CornFlowerBlue {{{const }}}}}}'''
#!if arg1_t_kw!=null
'''{{{#569cd6,#CornFlowerBlue {{{int }}}}}}'''
#!if arg1_t!=null
{{{#4ec9b0,#6fdbba {{{type1}}}}}}
#!if arg1_t_post!=null
{{{&}}}
#!if arg1_param != null && (arg1_kw != null || arg1_t_kw != null || arg1_t != null)
{{{ }}}
#!if arg1_param!=null
{{{#bcdce6 {{{...}}}}}}
#!if arg2_concept != null || arg2_kw != null || arg2_t_kw != null || arg2_t != null || arg2_param != null
{{{, }}}
#!if arg2_concept!=null
##======================================= argument 2
'''{{{#4ec9b0,#6fdbba {{{concept2}}}}}}'''{{{#!if arg2_concept_params!=null
{{{<}}}{{{#!if arg2_concept_tparam1!=null
{{{#4ec9b0,#6fdbba {{{T1}}}}}}}}}{{{#!if arg2_concept_tparam2!=null
{{{, }}}{{{#4ec9b0,#6fdbba {{{T2}}}}}}}}}{{{#!if arg2_concept_tparam3!=null
{{{, }}}{{{#4ec9b0,#6fdbba {{{T3}}}}}}}}}{{{>}}}}}}{{{ }}}
#!if arg2_kw!=null
'''{{{#569cd6,#CornFlowerBlue {{{const }}}}}}'''
#!if arg2_t_kw!=null
'''{{{#569cd6,#CornFlowerBlue {{{int }}}}}}'''
#!if arg2_t!=null
{{{#4ec9b0,#6fdbba {{{type2}}}}}}
#!if arg2_t_post!=null
{{{&}}}
#!if arg2_param!=null && (arg2_kw != null || arg2_t_kw != null || arg2_t != null)
{{{ }}}
#!if arg2_param!=null
{{{#bcdce6 {{{param2}}}}}}
#!if arg3_concept != null || arg3_kw != null || arg3_t_kw != null || arg3_t != null || arg3_param != null
{{{, }}}
#!if arg3_concept!=null
##======================================= argument 3
'''{{{#4ec9b0,#6fdbba {{{concept3}}}}}}'''{{{#!if arg3_concept_params!=null
{{{<}}}{{{#!if arg3_concept_tparam1!=null
{{{#4ec9b0,#6fdbba {{{T1}}}}}}}}}{{{#!if arg3_concept_tparam2!=null
{{{, }}}{{{#4ec9b0,#6fdbba {{{T2}}}}}}}}}{{{#!if arg3_concept_tparam3!=null
{{{, }}}{{{#4ec9b0,#6fdbba {{{T3}}}}}}}}}{{{>}}}}}}{{{ }}}
#!if arg3_kw!=null
'''{{{#569cd6,#CornFlowerBlue {{{const }}}}}}'''
#!if arg3_t_kw!=null
'''{{{#569cd6,#CornFlowerBlue {{{int }}}}}}'''
#!if arg3_t!=null
{{{#4ec9b0,#6fdbba {{{type3}}}}}}
#!if arg3_t_post!=null
{{{&}}}
#!if arg3_param!=null && (arg3_kw != null || arg3_t_kw != null || arg3_t != null)
{{{ }}}
#!if arg3_param!=null
{{{#bcdce6 {{{param3}}}}}}
#!if arg4_concept != null || arg4_kw != null || arg4_t_kw != null || arg4_t != null || arg4_param != null
{{{, }}}
#!if arg4_concept!=null
##======================================= argument4
'''{{{#4ec9b0,#6fdbba {{{concept3}}}}}}'''{{{#!if arg4_concept_params!=null
{{{<}}}{{{#!if arg4_concept_tparam1!=null
{{{#4ec9b0,#6fdbba {{{T1}}}}}}}}}{{{#!if arg4_concept_tparam2!=null
{{{, }}}{{{#4ec9b0,#6fdbba {{{T2}}}}}}}}}{{{#!if arg4_concept_tparam4!=null
{{{, }}}{{{#4ec9b0,#6fdbba {{{T3}}}}}}}}}{{{>}}}}}}{{{ }}}
#!if arg4_kw!=null
'''{{{#569cd6,#CornFlowerBlue {{{const }}}}}}'''
#!if arg4_t_kw!=null
'''{{{#569cd6,#CornFlowerBlue {{{int }}}}}}'''
#!if arg4_t!=null
{{{#4ec9b0,#6fdbba {{{type4}}}}}}
#!if arg4_t_post!=null
{{{&}}}
#!if arg4_param!=null && (arg4_kw != null || arg4_t_kw != null || arg4_t != null)
{{{ }}}
#!if arg4_param!=null
{{{#bcdce6 {{{param4}}}}}}
#!if arg5_param!=null
##======================================= argument5, argument6
{{{, }}}{{{#bcdce6 {{{param5}}}}}}
#!if arg6_param != null
{{{, }}}{{{#bcdce6 {{{param5}}}}}}
#!if arg_last_dots != null
{{{, ...}}}
#!if body_bclose!=null
##=======================================
{{{)}}}
#!if body_lnb!=null
[br]
#!if body_lnb!=null
{{{ }}}
#!if body_spec1!=null
'''{{{#DodgerBlue,#CornFlowerBlue {{{ const}}}}}}'''
#!if body_spec1_paren != null
{{{(}}}
#!if body_spec1_ref!=null
{{{&}}}
#!if body_spec2!=null
{{{#!if body_spec1!=null && body_spec1_paren == null
{{{ }}}}}}'''{{{#DodgerBlue,#CornFlowerBlue {{{noexcept}}}}}}'''
#!if body_spec2_paren != null
{{{(}}}
#!if body_spec2_label != null
{{{...}}}
#!if body_spec2_paren != null
{{{)}}}
#!if body_spec1_paren != null
{{{)}}}
#!if body_tmpclose!=null
##======================================= last labels
{{{>}}}
#!if label_last!=null
{{{}}}
#!if last!=null
{{{;}}}
[include(틀:C++ 요소, fn_attribute=특성, pre1_t=반환-자료형, body_f=함수-식별자,
arg1_t=매개변수1-자료형, arg1_param=매개변수1,
arg2_t=매개변수2-자료형, arg2_param=매개변수2, arg_last_dots=1,
body_bopen=1, body_bclose=1, last=;)]
[include(틀:C++ 요소, head_keyword=template,
template_p0=템플릿-매개변수1, template_p1=템플릿-매개변수2,
template_last_label=...)]
[include(틀:C++ 요소, fn_attribute=특성, pre1_t=반환-자료형, body_f=함수-식별자,
arg1_t=매개변수1-자료형, arg1_param=매개변수1,
arg2_t=매개변수2-자료형, arg2_param=매개변수2, arg_last_dots=1,
body_bopen=1, body_bclose=1, body_spec1=noexcept, body_spec1_ref=(...) , body_spec2=requires, body_spec2_paren=1, body_spec2_label=...)]
[include(틀:C++ 요소, head_keyword=template,
template_p0=템플릿-매개변수1, template_p1=템플릿-매개변수2,
template_last_label=...)]
#!if head_comment!=null
{{{#57A64A,#5EBD46 }}}
#!if head_lnb!=null
##======================================= include and import
[br]
#!if import!=null
'''{{{#DodgerBlue,#CornFlowerBlue {{{import }}}}}}'''{{{#C8865E {{{<>}}}}}}{{{;}}}
#!if include!=null
##======================================= the keyword at head (can be `template`)
{{{#include <>}}}
#!if (head_keyword_available = head_keyword != null)
##======================================= template parameters begin
'''{{{#DodgerBlue,#CornFlowerBlue {{{requires}}}}}}'''
#!if template_available = (template_p0 != null || template_v0 != null || template_p1 != null || template_v1 != null || template_p2 != null || template_v2 != null || template_p3 != null || template_v3 != null)
#!if template_available || template_last_label != null
##======================================= template parameter 0
##======================================= template parameter 0 concept
{{{<}}}{{{#!if template_concept0_available = (template_cpt0 != null)
'''{{{#4ec9b0,#6fdbba {{{ }}}}}}'''}}}{{{#!if template_concept0_params_available = (template_concept0_p0 != null || template_concept0_p1 != null || template_concept0_p2 != null || template_concept0_p3 != null)
{{{#!if template_concept0_params_available || template_concept0_last_label != null
{{{<}}}}}}{{{#!if template_concept0_params_available
##======================================= template parameter 0 concept's parameters (type or value)
{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept0_p0 != null || template_concept0_v0 != null) && (template_concept0_p1 != null || template_concept0_v1 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept0_p1 != null || template_concept0_v1 != null) && (template_concept0_p2 != null || template_concept0_v2 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept0_p2 != null || template_concept0_v2 != null) && (template_concept0_p3 != null || template_concept0_v3 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if template_concept0_last_label != null
{{{,...}}}}}}}}}{{{#!if template_concept0_params_available || template_concept0_last_label != null
{{{>}}}}}}}}}{{{#!if template_concept0_available
{{{ }}}}}}{{{#!if template_p0_available = (template_p0 != null)
{{{#!if !template_concept0_available
##======================================= template parameter 0 contents
'''{{{#569cd6,#CornFlowerBlue {{{typename}}}}}}'''}}}{{{}}}{{{#4ec9b0,#6fdbba {{{ }}}}}}
}}}{{{#!if template_v0_available = (template_v0 != null)
{{{#4ec9b0,#6fdbba {{{}}}}}}{{{}}}{{{#ffffff '''{{{ }}}'''}}}
##======================================= template parameter 0 finished
}}}{{{#!if (template_p0_available || template_v0_available) && (template_p1 != null || template_v1 != null)
##======================================= template parameter 1
{{{, }}}}}}{{{#!if template_concept1_available = (template_cpt1 != null)
'''{{{#4ec9b0,#6fdbba {{{ }}}}}}'''}}}{{{#!if template_concept1_params_available = (template_concept1_p0 != null || template_concept1_p1 != null || template_concept1_p2 != null || template_concept1_p3 != null)
{{{#!if template_concept1_params_available || template_concept1_last_label != null
{{{<}}}}}}{{{#!if template_concept1_params_available
##======================================= template parameter 1 concept's parameters (type or value)
{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept1_p0 != null || template_concept1_v0 != null) && (template_concept1_p1 != null || template_concept1_v1 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept1_p1 != null || template_concept1_v1 != null) && (template_concept1_p2 != null || template_concept1_v2 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept1_p2 != null || template_concept1_v2 != null) && (template_concept1_p3 != null || template_concept1_v3 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if template_concept1_last_label != null
{{{,...}}}}}}}}}{{{#!if template_concept1_params_available || template_concept1_last_label != null
{{{>}}}}}}}}}{{{#!if template_concept1_available
{{{ }}}}}}{{{#!if template_p1_available = (template_p1 != null)
{{{#!if !template_concept1_available
##======================================= template parameter 1 contents
'''{{{#569cd6,#CornFlowerBlue {{{typename}}}}}}'''}}}{{{}}}{{{#4ec9b0,#6fdbba {{{ }}}}}}}}}{{{#!if template_v1_available = (template_v1 != null)
##======================================= template parameter 1 finished
{{{#4ec9b0,#6fdbba {{{}}}}}}{{{}}}{{{#ffffff '''{{{ }}}'''}}}}}}{{{#!if (template_p1_available || template_v1_available) && (template_p2 != null || template_v2 != null)
##======================================= template parameter 2
{{{, }}}}}}{{{#!if template_concept2_available = (template_cpt1 != null)
'''{{{#4ec9b0,#6fdbba {{{ }}}}}}'''}}}{{{#!if template_concept2_params_available = (template_concept2_p0 != null || template_concept2_p1 != null || template_concept2_p2 != null || template_concept2_p3 != null)
{{{#!if template_concept2_params_available || template_concept2_last_label != null
{{{<}}}}}}{{{#!if template_concept2_params_available
##======================================= template parameter 2 concept's parameters (type or value)
{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept2_p0 != null || template_concept2_v0 != null) && (template_concept2_p1 != null || template_concept2_v1 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept2_p1 != null || template_concept2_v1 != null) && (template_concept2_p2 != null || template_concept2_v2 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if (template_concept2_p2 != null || template_concept2_v2 != null) && (template_concept2_p3 != null || template_concept2_v3 != null)
{{{, }}}}}}{{{#4ec9b0,#6fdbba {{{}}}}}}'''{{{#ffffff {{{}}}}}}'''{{{#!if template_concept2_last_label != null
{{{,...}}}}}}}}}{{{#!if template_concept2_params_available || template_concept2_last_label != null
{{{>}}}}}}}}}{{{#!if template_concept2_available
{{{ }}}}}}{{{#!if template_p2_available = (template_p2 != null)
{{{#!if !template_concept2_available
##======================================= template parameter 2 contents
'''{{{#569cd6,#CornFlowerBlue {{{typename}}}}}}'''}}}{{{}}}{{{#4ec9b0,#6fdbba {{{ }}}}}}
}}}{{{#!if template_v2_available = (template_v2 != null)
##======================================= template parameter 2 finished
{{{#4ec9b0,#6fdbba {{{}}}}}}{{{}}}{{{#ffffff '''{{{ }}}'''}}}}}}{{{#!if template_available && template_last_label != null
##======================================= template finished
{{{, }}}}}}{{{#!if template_last_label != null
{{{...}}}}}}{{{#!if template_available || template_last_label != null
{{{>}}}}}}{{{#!if do_template_linebreak = (template_lnb != null)
##======================================= template linebreak
[br]}}}{{{#!if do_template_linebreak ;nbsp
}}}
#!if head_keyword_available!=null
{{{ }}}
#!if fn_attribute!=null
##======================================= contents
[[C++/문법/특성|{{{#a8a8a8 {{{[[attr1]]}}}}}}]]
#!if fn_attribute_lnk!=null
[[C++/문법/특성#attr1|{{{#a8a8a8 {{{[[attr1]]}}}}}}]]
#!if fn_attribute_lnb!=null
[br]
#!if fn_attribute_lnb!=null ;nbsp
#!if kw1!=null
'''{{{#569cd6,#CornFlowerBlue {{{contexpr}}}}}}'''{{{#!if kw1_post!=null
{{{_kwp1}}}}}}{{{#!if kw1_post==null
{{{ }}}}}}
#!if kw2!=null
'''{{{#569cd6,#CornFlowerBlue {{{long long}}}}}}'''{{{#!if kw2post!=null
{{{&&}}}}}}{{{#!if kw2_post==null
{{{ }}}}}}
#!if cls_attribute!=null
[[C++/문법/특성|{{{#a8a8a8 {{{[[attr2]]}}}}}}]]{{{ }}}
#!if cls_attribute_lnk!=null
[[C++/문법/특성#attr2|{{{#a8a8a8 {{{[[attr2]] }}}}}}]]
#!if ns1!=null
##======================================= namespaces
'''{{{#58fafe {{{std}}}}}}'''
#!if ns2!=null
{{{::}}}'''{{{#58fafe {{{chrono}}}}}}'''
#!if ns1!=null
{{{::}}}
#!if pre1_t!=null
##======================================= types at the front
{{{#4ec9b0,#6fdbba {{{system_clock }}}}}}
#!if pre2_t!=null
{{{::}}}{{{#4ec9b0,#6fdbba {{{duration }}}}}}
#!if pre_e!=null
{{{::}}}{{{#f0f068 {{{enum }}}}}}
#!if pre_post!=null
{{{_pre}}}
#!if pre_lnb!=null
[br]
#!if do_pre_linebreak = (pre_lnb != null)
##======================================= pre-body things finished
#!if (!do_pre_linebreak && !do_template_linebreak && head_keyword_available) && (body_v != null || body_f != null || body_mv != null || body_mf != null || body_post != null)
{{{ }}}
#!if body_v!=null
##======================================= identifiers of variable and function
{{{#a8a8a8,#a1a1a1 {{{val}}}}}}
#!if body_mv!=null
{{{#ffffff {{{mem_val}}}}}}
#!if body_f!=null
{{{#f87a7a {{{fun}}}}}}
#!if body_mf!=null
{{{#f0a962 {{{mem_fn}}}}}}
#!if body_post!=null
##======================================= argument 1
{{{}}}
#!if body_tmpopen!=null
{{{<}}}
#!if body_bopen!=null
{{{(}}}
#!if arg1_concept!=null
'''{{{#4ec9b0,#6fdbba {{{concept1}}}}}}'''{{{#!if arg1_concept_params!=null
{{{<}}}{{{#!if arg1_concept_tparam1!=null
{{{#4ec9b0,#6fdbba {{{T1}}}}}}}}}{{{#!if arg1_concept_tparam2!=null
{{{, }}}{{{#4ec9b0,#6fdbba {{{T2}}}}}}}}}{{{#!if arg1_concept_tparam3!=null
{{{, }}}{{{#4ec9b0,#6fdbba {{{T3}}}}}}}}}{{{>}}}}}}{{{ }}}
#!if arg1_kw!=null
'''{{{#569cd6,#CornFlowerBlue {{{const }}}}}}'''
#!if arg1_t_kw!=null
'''{{{#569cd6,#CornFlowerBlue {{{int }}}}}}'''
#!if arg1_t!=null
{{{#4ec9b0,#6fdbba {{{type1}}}}}}
#!if arg1_t_post!=null
{{{&}}}
#!if arg1_param != null && (arg1_kw != null || arg1_t_kw != null || arg1_t != null)
{{{ }}}
#!if arg1_param!=null
{{{#bcdce6 {{{...}}}}}}
#!if arg2_concept != null || arg2_kw != null || arg2_t_kw != null || arg2_t != null || arg2_param != null
{{{, }}}
#!if arg2_concept!=null
##======================================= argument 2
'''{{{#4ec9b0,#6fdbba {{{concept2}}}}}}'''{{{#!if arg2_concept_params!=null
{{{<}}}{{{#!if arg2_concept_tparam1!=null
{{{#4ec9b0,#6fdbba {{{T1}}}}}}}}}{{{#!if arg2_concept_tparam2!=null
{{{, }}}{{{#4ec9b0,#6fdbba {{{T2}}}}}}}}}{{{#!if arg2_concept_tparam3!=null
{{{, }}}{{{#4ec9b0,#6fdbba {{{T3}}}}}}}}}{{{>}}}}}}{{{ }}}
#!if arg2_kw!=null
'''{{{#569cd6,#CornFlowerBlue {{{const }}}}}}'''
#!if arg2_t_kw!=null
'''{{{#569cd6,#CornFlowerBlue {{{int }}}}}}'''
#!if arg2_t!=null
{{{#4ec9b0,#6fdbba {{{type2}}}}}}
#!if arg2_t_post!=null
{{{&}}}
#!if arg2_param!=null && (arg2_kw != null || arg2_t_kw != null || arg2_t != null)
{{{ }}}
#!if arg2_param!=null
{{{#bcdce6 {{{param2}}}}}}
#!if arg3_concept != null || arg3_kw != null || arg3_t_kw != null || arg3_t != null || arg3_param != null
{{{, }}}
#!if arg3_concept!=null
##======================================= argument 3
'''{{{#4ec9b0,#6fdbba {{{concept3}}}}}}'''{{{#!if arg3_concept_params!=null
{{{<}}}{{{#!if arg3_concept_tparam1!=null
{{{#4ec9b0,#6fdbba {{{T1}}}}}}}}}{{{#!if arg3_concept_tparam2!=null
{{{, }}}{{{#4ec9b0,#6fdbba {{{T2}}}}}}}}}{{{#!if arg3_concept_tparam3!=null
{{{, }}}{{{#4ec9b0,#6fdbba {{{T3}}}}}}}}}{{{>}}}}}}{{{ }}}
#!if arg3_kw!=null
'''{{{#569cd6,#CornFlowerBlue {{{const }}}}}}'''
#!if arg3_t_kw!=null
'''{{{#569cd6,#CornFlowerBlue {{{int }}}}}}'''
#!if arg3_t!=null
{{{#4ec9b0,#6fdbba {{{type3}}}}}}
#!if arg3_t_post!=null
{{{&}}}
#!if arg3_param!=null && (arg3_kw != null || arg3_t_kw != null || arg3_t != null)
{{{ }}}
#!if arg3_param!=null
{{{#bcdce6 {{{param3}}}}}}
#!if arg4_concept != null || arg4_kw != null || arg4_t_kw != null || arg4_t != null || arg4_param != null
{{{, }}}
#!if arg4_concept!=null
##======================================= argument4
'''{{{#4ec9b0,#6fdbba {{{concept3}}}}}}'''{{{#!if arg4_concept_params!=null
{{{<}}}{{{#!if arg4_concept_tparam1!=null
{{{#4ec9b0,#6fdbba {{{T1}}}}}}}}}{{{#!if arg4_concept_tparam2!=null
{{{, }}}{{{#4ec9b0,#6fdbba {{{T2}}}}}}}}}{{{#!if arg4_concept_tparam4!=null
{{{, }}}{{{#4ec9b0,#6fdbba {{{T3}}}}}}}}}{{{>}}}}}}{{{ }}}
#!if arg4_kw!=null
'''{{{#569cd6,#CornFlowerBlue {{{const }}}}}}'''
#!if arg4_t_kw!=null
'''{{{#569cd6,#CornFlowerBlue {{{int }}}}}}'''
#!if arg4_t!=null
{{{#4ec9b0,#6fdbba {{{type4}}}}}}
#!if arg4_t_post!=null
{{{&}}}
#!if arg4_param!=null && (arg4_kw != null || arg4_t_kw != null || arg4_t != null)
{{{ }}}
#!if arg4_param!=null
{{{#bcdce6 {{{param4}}}}}}
#!if arg5_param!=null
##======================================= argument5, argument6
{{{, }}}{{{#bcdce6 {{{param5}}}}}}
#!if arg6_param != null
{{{, }}}{{{#bcdce6 {{{param5}}}}}}
#!if arg_last_dots != null
{{{, ...}}}
#!if body_bclose!=null
##=======================================
{{{)}}}
#!if body_lnb!=null
[br]
#!if body_lnb!=null
{{{ }}}
#!if body_spec1!=null
'''{{{#DodgerBlue,#CornFlowerBlue {{{ const}}}}}}'''
#!if body_spec1_paren != null
{{{(}}}
#!if body_spec1_ref!=null
{{{&}}}
#!if body_spec2!=null
{{{#!if body_spec1!=null && body_spec1_paren == null
{{{ }}}}}}'''{{{#DodgerBlue,#CornFlowerBlue {{{noexcept}}}}}}'''
#!if body_spec2_paren != null
{{{(}}}
#!if body_spec2_label != null
{{{...}}}
#!if body_spec2_paren != null
{{{)}}}
#!if body_spec1_paren != null
{{{)}}}
#!if body_tmpclose!=null
##======================================= last labels
{{{>}}}
#!if label_last!=null
{{{}}}
#!if last!=null
{{{;}}}
[include(틀:C++ 요소, cls_attribute=특성,
kw1=class, pre1_t=클래스-식별자,
last=;)]
requires
식은 템플릿에 바로 조건을 달 때 사용할 수 있다. 함수 템플릿, 클래스 템플릿, 멤버 함수 템플릿에 사용할 수 있다. 심지어 생성자나 소멸자에도 사용할 수 있다. 생성자와 소멸자를 선택한 후보만 평가되도록 만들 수 있다.7. 예제 2: 클래스 기본 멤버 함수#!syntax cpp
template<typename T>
class Wrapper
{
private:
T value;
};
클래스 문서에서 봤던 클래스의 명세를 개념과 requires
식을 통해 전부 드러내보자. 상기한 코드는 값 하나를 감싸는 클래스를 보여주고 있다. 대부분의 경우 컴파일러가 이 구조체에 필요한 모든 사항을 알아서 해줄 것이다. 대부분은 99.99%의 경우를 말한다. 그런데 `T`
가 원시 자료형이 아니라 복잡한 구조체의 경우 구조체를 이리저리 조작할 때 문제가 생길 수 있다. 멀리 가는게 아니라 동적할당한 메모리를 어디에다 복사할 때 문제가 생길 수 있다. 이를 해결하려면 복사한 메모리에 든 내용을 확인하거나, 어떤 걸 복사하는지, 어떻게 복사하는지, 언제 복사하는지를 확인해야한다. 이때 디버깅을 쉽게 하려면 각 경우의 수를 줄여나가는 방법이 있다. 가령 어떤 걸 복사하는지 알 수 있다면 나머지는 실마리가 쉽게 풀린다. 클래스의 정보를 알 수 있으면 어떤 걸 복사하는지 확인할 수 있다. 이제 예제를 보자.#!syntax cpp
import <concepts>;
template<typename T>
class Wrapper
{
public:
constexpr Wrapper() noexcept(std::is_nothrow_default_constructible_v<T>) requires (std::default_initializable<T>) = default;
private:
T value;
};
먼저 생성자를 보자. 기본 생성자를 가지려면 기본 초기화(Default Initialization)가 가능해야 한다. 예외 사양도 달아주었다.#!syntax cpp
constexpr ~Wrapper() noexcept(std::is_nothrow_destructible<T>) requires (std::is_destructible_v<T>) = default;
소멸자는 개념 std::destructible<T>
대신 메타 함수 std::is_destructible_v<T>
를 썼는데, 이유는 std::destructible<T>
은 예외가 없는 것 까지 포함해서 검사하기 때문이다. 소멸자의 존재 자체만 검사하면 되므로 예외 여부는 필요없다.#!syntax cpp
constexpr Wrapper(const Wrapper&) noexcept(std::is_nothrow_copy_constructible_v<T>) requires (std::copy_constructible<T>) = default;
복사 생성자는 위와 같다.#!syntax cpp
constexpr Wrapper& operator=(const Wrapper&) noexcept(std::is_nothrow_copy_assignable_v<T>) requires (std::assignable_from<T&, const T&>) = default;
복사 대입 연산자는 위와 같다.#!syntax cpp
constexpr Wrapper(Wrapper&&) noexcept(std::is_nothrow_move_constructible_v<T>) requires (std::move_constructible<T>) = default;
이동 생성자는 위와 같다.#!syntax cpp
constexpr Wrapper& operator=(Wrapper&&) noexcept(std::is_nothrow_move_assignable_v<T>) requires (std::assignable_from<T&, T>) = default;
이동 대입 연산자는 위와 같다.#!syntax cpp
constexpr bool operator==(const T&) noexcept(noexcept(std::declval<const T&>() == std::declval<const T&>())) requires (std::equality_comparable<T>) = default;
동등 비교 연산자는 위와 같다.#!syntax cpp
constexpr auto operator<=>(const T&) noexcept(noexcept(std::declval<const T&>() <=> std::declval<const T&>())) requires (std::three_way_comparable<T>) = default;
삼항 비교 연산자는 위와 같다.#!syntax cpp
import <type_traits>;
import <concepts>;
template<typename T>
class [[nodiscard]] Wrapper
{
public:
constexpr Wrapper() noexcept(std::is_nothrow_default_constructible_v<T>) requires (std::default_initializable<T>) = default;
constexpr ~Wrapper() noexcept(std::is_nothrow_destructible<T>) requires (std::is_destructible_v<T>) = default;
constexpr Wrapper(Wrapper&&) noexcept(std::is_nothrow_move_constructible_v<T>) requires (std::move_constructible<T>) = default;
constexpr Wrapper(const Wrapper&) noexcept(std::is_nothrow_copy_constructible_v<T>) requires (std::copy_constructible<T>) = default;
constexpr Wrapper& operator=(const Wrapper&) noexcept(std::is_nothrow_copy_assignable_v<T>) requires (std::assignable_from<T&, const T&>) = default;
constexpr Wrapper& operator=(Wrapper&&) noexcept(std::is_nothrow_move_assignable_v<T>) requires (std::assignable_from<T&, T>) = default;
[[nodiscard]]
constexpr bool operator==(const T&) noexcept(noexcept(std::declval<const T&>() == std::declval<const T&>())) requires (std::equality_comparable<T>) = default;
[[nodiscard]]
constexpr auto operator<=>(const T&) noexcept(noexcept(std::declval<const T&>() <=> std::declval<const T&>())) requires (std::three_way_comparable<T>) = default;
[[nodiscard]]
explicit constexpr operator T& () & noexcept
{
return value;
}
[[nodiscard]]
explicit constexpr operator const T& () const & noexcept
{
return value;
}
[[nodiscard]]
explicit constexpr operator T&& () && noexcept
{
return std::move(value);
}
[[nodiscard]]
explicit constexpr operator const T&& () const&& noexcept
{
return std::move(value);
}
private:
T value;
};
변환 연산자 까지 추가한 완성본은 위와 같다.8. 제약조건의 도입 목적 개념은 사실 다른 언어에서 보이는 인터페이스(Interface)를 C++ 의 방식대로 도입한 물건이다. C++에도 virtual
을 비롯한 동적 바인딩과 다중 상속 으로 똑같은 기능을 지원하지만 성능 문제로 못 쓰는 경우가 있다. 이미 이런식으로 정적인(컴파일 시간에 평가되는) 기능, 동적인(실행 시간에 평가되는) 기능이 양립하는 사례가 있다. 가령 static_assert(expr)
과 C++26
의 contract_assert(expr)
의 사례가 있다.<C++ 예제 보기> #!syntax cpp
template<typename T>
concept Lockable = requires(T& locker)
{
locker.Lock();
locker.Unlock();
};
struct UniqueLock
{
void Lock();
void Unlock();
};
struct SharedLock
{
void Lock();
void Unlock();
};
// 두 클래스엔 공통 상속 관계는 없지만 둘 다 개념 `Lockable`을 만족한다.
개념은 템플릿에 조건을 달고 싶고, 더러운 코드가 싫고, SFINAE 가 싫고, 다중 상속이 싫고, virtual
이 싫은 사람들에게 딱인 기능이며 이 조건들은 AND 가 아니다. 템플릿 메타 프로그래밍이라는 엄청 거창하고 깊은 영역에서만 쓰는 게 아니라, 단순히 클래스의 규격을 통일하는 용도로도 쓸 수 있다는 것이다. 표준 라이브러리 는 상속을 쓰는 대신 개념을 써서 자료형을 제한하도록 명세가 짜여있다. 여담으로 개념과 requires
식은 내부적으로는 SFINAE 로 구현되지만 역으로 이들을 변수나 함수 템플릿 혹은 SFINAE에 써먹을 수 있다. 허나 메타 프로그래밍 문서에서 관련 내용을 소개하고 여기서는 새로운 기능에 관해서만 설명한다.9. 예제 3: 함자에 튜플로 인자 전달하기 이번에 구현할 예제는 함자에 튜플 로 다수에 인자를 전달해서 실행시키는 함수다. 가령 함자 인스턴스와 튜플 하나를 받는 함수를 생각해볼 수 있다. 이때 제약조건을 써서 사용조건을 명확히 하고 오류가 떠도 쉽게 읽을 수 있도록 하려고 한다. 참고로 이 함수는 표준 라이브러리에 std::apply(...)
로 구현되어 있으므로 참고.#!syntax cpp
template<typename Functor, typename... Args>
auto Apply(Functor fun, std::tuple<Args...>& params);
튜플 안에 있는 Args...
는 부패 가 일어나지 않으므로 참조자가 증발할 염려는 안해도 된다. 하지만 걱정거리가 부패 하나 뿐일까? 함자가 복사할 수 없는 클래스라면 이 함수는 못 쓰지 않을까?#!syntax cpp
struct Invoker
{
void operator()();
Manager& mgr;
};
template<typename Functor, typename... Args>
auto Apply(Functor fun, std::tuple<Args...>& params);
예를 들어서 상기 코드의 클래스 `Invoker`
를 사용할 수 없다. 참조자를 데이터 멤버로 갖고 있기 때문이다.#!syntax cpp
template<typename Functor, typename... Args>
auto Apply(Functor&& fun, std::tuple<Args...>& params);
완벽한 전달을 하도록 고쳤다. 그런데 튜플을 lvalue 만 받아야 할까? Apply(func, std::make_tuple(...));
처럼 rvalue 튜플도 바로 받을 수 있어야하지 않을까? 그리고 상수 튜플도 받을 수 있어야 할 것이다. 그러면 오버로딩을 해보자.#!syntax cpp
template<typename Functor, typename... Args>
auto Apply(Functor&& fun, std::tuple<Args...>& params);
template<typename Functor, typename... Args>
auto Apply(Functor&& fun, const std::tuple<Args...>& params);
template<typename Functor, typename... Args>
auto Apply(Functor&& fun, std::tuple<Args...>&& params);
template<typename Functor, typename... Args>
auto Apply(Functor&& fun, const std::tuple<Args...>&& params);
이 내용을 다 구현하려다가는 C++의 불합리함을 통탄하며 관둘거다. 일거리를 줄이려면 템플릿으로 튜플의 인자를 압축하고 완벽한 전달을 하는 수 밖에 없다.#!syntax cpp
template<typename Functor, typename Tuple>
decltype(auto) Apply(Functor&& fun, Tuple&& params);
이 안에서 해결해보자. 표준 라이브러리에 이미 튜플 관련 유틸리티가 있음을 기억하자.#!syntax cpp
template<typename Functor, typename Tuple>
decltype(auto) Apply(Functor&& fun, Tuple&& params)
{
return ApplyImpl(...);
}
막막한데 템플릿 문서에서 매개변수 열거를 하던 방법대로 해보자. 내부 구현 함수를 따로 두는 것이다. 내부 구현 함수는 함자, 튜플, 튜플의 크기 속성을 받아 열거하는 기능을 한다.#!syntax cpp
template<typename F, typename Tuple, std::size_t... Indices>
decltype(auto) ApplyImpl(F&&, Tuple&&, std::index_sequence<Indices...>);
template<typename Functor, typename Tuple>
decltype(auto) Apply(Functor&& fun, Tuple&& params)
{
return ApplyImpl(std::forward<Functor>(fun), std::forward<Tuple>(params), std::make_index_sequence<std::tuple_size_v<Tuple>>{});
}
튜플의 크기를 구하는 메타 함수 std::tuple_size_v
를 std::make_index_sequence<Count>
에 전달해서 튜플의 길이를 전달했다. 이 과정은 컴파일 시간에 평가되며 성능에 아무런 영향이 없다.#!syntax cpp
template<typename F, typename Tuple, std::size_t... Indices>
decltype(auto) ApplyImpl(F&& fn, Tuple&& tuple, std::index_sequence<Indices...>)
{
return std::invoke(std::forward<F>(fn), std::get<Indices>(std::forward<Tuple>(tuple))...);
}
template<typename Functor, typename Tuple>
decltype(auto) Apply(Functor&& fun, Tuple&& params)
{
return ApplyImpl(std::forward<Functor>(fun), std::forward<Tuple>(params), std::make_index_sequence<std::tuple_size_v<Tuple>>{});
}
void Test(int a, double b)
{
std::println("{}, {}", a, b);
}
int TestR(int a, double b)
{
std::println("{}, {}", a, b);
return 80;
}
int main()
{
// (1) 반환형이 없는 함수
// `Functor`는 void(&)(int, double)
// `Tuple`은 std::tuple<int, int>&&
// 템플릿 명세는 template<void(&)(int, double), std::tuple<int, int>>
// 함수 서명은 void(void(&)(int, double), std::tuple<int, int>&&)
::Apply(Test, std::make_tuple(0, 10));
// (2) 반환형이 있는 함수
// `Functor`는 int(&)(int, double)
// `Tuple`은 std::tuple<long, float>&&
// 템플릿 명세는 template<int(&)(int, double), std::tuple<long, float>>
// 함수 서명은 int(int(&)(int, double), std::tuple<long, float>&&)
// `result0`의 값은 80
int result0 = ::Apply(TestR, std::make_tuple(20L, 40.0f));
// (3) 람다 표현식과 rvalue
// `Functor`는 lambda[](long, unsigned) -> char
// `Tuple`은 std::tuple<long long, unsigned int>&&
// 템플릿 명세는 template<lambda&&, std::tuple<long long, unsigned int>>
// 함수 서명은 char(char(&&)(long, unsigned), std::tuple<long long, unsigned int>&&)
// `result1`의 값은 'A'
char result1 = ::Apply([](long, unsigned) -> char { return 'A'; }, std::make_tuple(160LL, 320U));
// (4) 람다 표현식과 lvalue
// `Functor`는 lambda[](long, unsigned) -> void
// `Tuple`은 const std::tuple<long long, unsigned int>&
// 템플릿 명세는 template<lambda&&, const std::tuple<long long, unsigned int>&>
// 함수 서명은 '오류'
// 컴파일 오류! ApplyImpl의 함수 템플릿 인스턴스가 없습니다.
const auto params = std::make_tuple(160LL, 320U);
::Apply([](long, unsigned) {}, params);
}
완성된 함수는 위 코드와 같다. 헌데 마지막 사례가 이상하다. 한정자를 덕지덕지 붙이긴 했으나 lvalue를 사용할 수 없다. 이유는 std::tuple_size_v
는 내부적으로 받은 자료형을 부패시키지 않는데, const
와 volatile
에 대하여 특수화가 되어있지 않아서 그렇다. 이럴 때는 우리가 직접 부패를 시키면 된다.#!syntax cpp
template<typename Functor, typename Tuple>
decltype(auto) Apply(Functor&& fun, Tuple&& params)
{
return ApplyImpl(std::forward<Functor>(fun), std::forward<Tuple>(params), std::make_index_sequence<std::tuple_size_v<std::decay_t<Tuple>>>{});
}
이 정도면 된 듯한데, 아직 멀었다. 함수에 아무런 매개변수가 없는 경우도 상정해야 한다. 그럼 함수 템플릿 오버로딩을 하면 될까? 이 참에 함자도 바로 실행시키자.#!syntax cpp
template<typename Functor>
decltype(auto) Apply(Functor&& fun, std::tuple<>)
{
return std::invoke(std::forward<Functor>(fun));
}
template<typename Functor>
decltype(auto) Apply(Functor&& fun)
{
return std::invoke(std::forward<Functor>(fun));
}
그런데 이대로 두면 매개변수가 없는 함수를 따로 구별할 방법이 없다. 함수에 아무 인자나 넣을 수 있기 때문이다. 이럴때 제약조건을 사용하면 된다.#!syntax cpp
import <concepts>;
template<std::invocable Functor>
decltype(auto) Apply(Functor&& fun, std::tuple<>)
{
return std::invoke(std::forward<Functor>(fun));
}
template<std::invocable Functor>
decltype(auto) Apply(Functor&& fun)
{
return std::invoke(std::forward<Functor>(fun));
}
표준 라이브러리의 std::invocable<F, Args...>
을 사용할 수 있다. 이 개념은 함자 자료형 `F`
가 인자로 Args...
를 받아 실행할 수 있는지를 검사한다. 예제에서는 아무 가변 인자를 전달하지 않았는데, 이 경우 `Functor`
에 아무런 인자를 넣지 않아도 실행할 수 있는지를 검사한다. 이제 오버로딩 후보의 모호함도 사라진다.#!syntax cpp
void TestEmpty()
{
std::println("Empty function");
}
int main()
{
// (1) 문제없음.
::Apply(TestEmpty);
// (2) 문제없음.
::Apply(TestEmpty, std::make_tuple());
// (3) 컴파일 오류!
::Apply(TestEmpty, std::make_tuple(1));
}
다 좋은데, 불편한 점이 있다. (3)번에서 오류를 바로 알 수 없어서 불편하다. 무슨 소리인가 싶겠지만, 일단 컴파일을 해야 오류를 띄워주므로 IDE 상에서 얼핏 보기만 해선 문제가 있는지 알 수가 없다. SFINAE 의 장점 중 하나는 조건을 만족하지 않는 템플릿 인스턴스를 즉시 제거하는 거였는데 제약조건으로도 마찬가지 행위가 가능하다.#!syntax cpp
template<typename F, typename Tuple, std::size_t... Indices>
requires std::invocable<F, std::tuple_element_t<Indices, Tuple>...>
decltype(auto) ApplyImpl(F&& fn, Tuple&& tuple, std::index_sequence<Indices...>)
{
return std::invoke(std::forward<F>(fn), std::get<Indices>(std::forward<Tuple>(tuple))...);
}
여러가지 방법이 있겠지만 일단 조건을 걸기 쉬운 경우를 보자. 내부 구현 함수 `ApplyImpl`
에 requires
식으로 제약조건을 걸었다. `ApplyImpl`
에 잘못된 인자가 들어오면 컴파일 시점에 즉시 알 수 있으며, 오류 메시지도 친절하게 표시된다. 예제에서는 표준 라이브러리의 std::tuple_element_t<I, Tuple>
을 썼다 .#!syntax cpp
// 여기엔 조건을 어떻게 걸까?
template<typename Functor, typename Tuple>
decltype(auto) Apply(Functor&& fun, Tuple&& params);
그러나 여전히 문제는 해결되지 않는다. 내부적으로 템플릿을 검사 하는 건 그렇다 치지만, 그것보다는 본 함수 `Apply`
에서 문제를 바로 띄우고 싶기 때문이다. 그러나 이미 작성한 템플릿은 std::tuple<Ts...>
을 압축했는데, 이걸 풀면 함수 오버로딩을 전부 따로 구현해줘야 한다. 이미 복잡한 코드인데 더 나누면 유지보수 가용성은 나락으로 간다. 우리는 템플릿 매개변수 `Tuple`
로 압축했던 튜플의 정보를 메타 프로그래밍으로 다시 꺼내와야 한다. 아쉽지만 제약조건만으로는 해결되지 않는 경우다. 한편 매개변수가 없는 경우는 앞전에 특수화를 했으므로 고려하지 않아도 된다. std::invocable
이 알아서 검사해준다.#!syntax cpp
template<typename Functor, typename Params, typename Indexer>
struct TupleAppliableImpl;
먼저 메타 함수를 정의하자. `Functor`
는 함자, `Params`
는 튜플이 들어올 예정이고, `Indexer`
는 튜플의 정보를 담을 std::index_sequence
다.#!syntax cpp
template<typename Functor, typename Params, typename Indexer>
struct TupleAppliableImpl : std::bool_constant<bool> {};
그럼 우리가 할일은 무엇일까? 이 메타 함수의 목적은 튜플을 적용할 수 있는지 없는지 여부를 검사하는 것이다. 곧 이 메타 함수가 어떤 bool
을 반환할 수 있으면 된다. 메타 프로그래밍을 시도할 것이므로 정적 데이터 멤버로 bool
값을 가지면 된다. 여러 대안이 있겠으나 여기서는 표준 라이브러리 의 bool_constant<bool>
을 상속받는다. 이때 검사할 bool
값은 `Functor`
가 `Params`
에 든 원소를 인자로 받아서 실행시킬 수 있느냐 아니냐다. 우리가 앞서 본 템플릿 문서의 튜플 예제에서는 자료형 트레잇이 기본값을 가지는 것이 장점이라고 언급한 바 있다. 기본값을 가지는 구조체를 상속하고, 조건을 검사하는 부분을 특수화하는 식으로 구현도 했었다. 내부 구현 함수에 함자, 튜플, 튜플의 크기 속성을 매개변수로 만들었었다. 이제 똑같이 템플릿으로 함자, 튜플, 튜플의 크기 속성을 전달하면 된다. 어떻게 구현하면 될까? 방식은 다음 두가지가 있다.#!syntax cpp
// (1) 기본적으로 모든 상황에 대하여 true, 조건을 만족하지 않는 경우는 false
template<typename Functor, typename Params, typename Indexer>
struct TupleAppliableImpl : std::true_type {};
// (2) 기본적으로 모든 상황에 대하여 false, 조건을 만족하는 경우는 true
template<typename Functor, typename Params, typename Indexer>
struct TupleAppliableImpl : std::false_type {};
두 경우의 구현 난이도는 동일하다. 그러나 구현 목적을 생각해본다면 후자가 더 합리적이다. 조건을 만족할 때만 걸러낸다는(Filter) 목적에 부합한다. 그럼 검사할 상황에 대해 특수화 해보자.#!syntax cpp
template<typename Functor, typename Params, std::size_t... Indices>
struct TupleAppliableImpl<Functor, Params, std::index_sequence<Indices...>>;
함자, 튜플, 열거용 튜플의 길이 등 각각 매개변수가 잘 들어왔다고 가정한다. 이 특수화의 경우도 bool_constant<bool>
를 상속받아야 한다.#!syntax cpp
template<typename Functor, typename Params, std::size_t... Indices>
struct TupleAppliableImpl<Functor, Params, std::index_sequence<Indices...>>
: std::bool_constant<std::invocable<Functor, std::tuple_element_t<Indices, Params>...>>
{};
상기 코드에서는 표준 라이브러리의 std::tuple_element_t
를 써서 튜플의 성분 자료형을 열거한다. 표준 라이브러리의 std::invocable
을 썼다. 사실 동일한 기능의 메타 함수로 std::is_invocable_v<F, Args...>
가 있긴한데, 개념을 쓰면 더 읽기 쉽다.#!syntax cpp
template<typename Functor, typename Params>
using TupleAppliable = TupleAppliableImpl<Functor, Params, std::make_index_sequence<std::tuple_size_v<std::decay_t<Params>>>>;
template<typename Functor, typename Params>
constexpr bool TupleAppliable_v = TupleAppliable<Functor, Params>::value;
그리고 유틸리티 템플릿도 정의하자.#!syntax cpp
void Test(int a, double b);
int TestR(int a, double b);
void Test2();
// (1) TupleAppliable_v은 true
TupleAppliable_v<decltype(Test), std::tuple<int, int>>;
// (2) TupleAppliable_v은 false
TupleAppliable_v<decltype(Test), std::tuple<std::string, int>>;
// (3) TupleAppliable_v은 true
TupleAppliable_v<decltype(TestR), std::tuple<int, int>>;
// (4) TupleAppliable_v은 true
TupleAppliable_v<decltype(Test2), std::tuple<>>;
상기 코드는 유틸리티를 실행한 결과를 보여주고 있다. 이제 제약조건을 통해 함수 `Apply`
에 적용해보자.#!syntax cpp
template<typename Functor, typename Tuple>
requires TupleAppliable_v<Functor&&, Tuple>
decltype(auto) Apply(Functor&& fun, Tuple&& params)
{
return ApplyImpl(std::forward<Functor>(fun), std::forward<Tuple>(params), std::make_index_sequence<std::tuple_size_v<std::decay_t<Tuple>>>{});
}
위와 같이 고치면 된다. 그런데 문제가 또 생긴다. 앞서 정의한 오버로딩에서 컴파일 오류가 뜬다.#!syntax cpp
// 컴파일 오류!
// 중복 정의된 함수 템플릿
template<typename Functor>
decltype(auto) Apply(Functor&& fun, std::tuple<>);
왜냐하면 이제 원본 함수 템플릿이 특수화한 경우까지 모두 포괄하기 때문이다. 이 오버로딩은 삭제해도 된다.#!syntax cpp
void Test(int a, double b);
int TestR(int a, double b);
void Test2();
template<typename F, typename Tuple, std::size_t... Indices>
// 이제 `ApplyImpl`은 굳이 제약조건을 가질 이유가 없으므로 requires 식을 삭제함.
decltype(auto) ApplyImpl(F&& fn, Tuple&& tuple, std::index_sequence<Indices...>)
{
return std::invoke(std::forward<F>(fn), std::get<Indices>(std::forward<Tuple>(tuple))...);
}
template<typename Functor, typename Params, typename Indexer>
struct TupleAppliableImpl : std::false_type {};
template<typename Functor, typename Params, std::size_t... Indices>
struct TupleAppliableImpl<Functor, Params, std::index_sequence<Indices...>>
: std::bool_constant<std::invocable<Functor, std::tuple_element_t<Indices, Params>...>>
{};
template<typename Functor, typename Params>
using TupleAppliable = TupleAppliableImpl<Functor
// `Params`을 부패시키도록 고침.
, std::decay_t<Params>
, std::make_index_sequence<std::tuple_size_v<std::decay_t<Params>>>>;
template<typename Functor, typename Params>
constexpr bool TupleAppliable_v = TupleAppliable<Functor, Params>::value;
template<typename Functor, typename Tuple>
requires TupleAppliable_v<Functor&&, Tuple>
decltype(auto) Apply(Functor&& fun, Tuple&& params)
{
return ApplyImpl(std::forward<Functor>(fun), std::forward<Tuple>(params), std::make_index_sequence<std::tuple_size_v<std::decay_t<Tuple>>>{});
}
template<std::invocable Functor>
decltype(auto) Apply(Functor&& fun)
{
return std::invoke(std::forward<Functor>(fun));
}
int main()
{
::Apply(Test2);
::Apply(Test2, std::make_tuple());
// 컴파일 오류! 함수 템플릿 인스턴스가 없습니다.
::Apply(Test2, std::make_tuple(1));
::Apply(Test, std::make_tuple(0, 10));
::Apply(TestR, std::make_tuple(0, 10));
char result1 = ::Apply([](long, unsigned) -> char { return 'A'; }, std::make_tuple(160LL, 320U));
const std::tuple params = std::make_tuple(160LL, 320U);
::Apply([](long long, unsigned) {}, params);
::Apply(Test, params);
}
살짝 정리하고 완성된 코드는 위와 같다. lvalue도 쓸 수 있고, 람다도 가능하며, 반환값도 잘 들어온다. 그리고 틀린 사용례가 나오면 바로 알려준다.#!syntax cpp
// 상수 템플릿을 써서 개념 정의함.
template<typename Params, typename Functor>
concept AppliableTuples = TupleAppliable_v<Functor, Params>;
template<typename Functor, AppliableTuples<Functor> Tuple>
decltype(auto) Apply(Functor&& fun, Tuple&& params);
requires
식은 편리하지만, 그냥 함수 인스턴스가 없다고만 띄워주기에 오류 내용을 한번 들어가봐야 하는 사소한 단점이 있다. 컴파일러에 따라 다르지만, 개념을 쓰면 `템플릿 매개변수가 개념 'AppliableTuples'을 만족하지 않았습니다.` 식의 오류 메시지를 띄워줄 수 있다.10. 예제 4: 선택적 함자 실행 앞서 구현한 함수는 잘 작동하지만, 단순하게 사용할 수 없으면 함수 인스턴스를 제거하는 식이었다. 거기까지만 해도 유틸리티 용으로는 차고 넘치지만, 한 발짝 더 나아가보자. 이번엔 실행할 수 있으면 실행한 반환값을 내보내고, 실행할 수 없으면 오류 값을 내보내는 함수를 구현해보자. [include(틀:C++ 요소, head_keyword=template, template_p0=SuccessType, template_p1=FailureType, template_lnb=1, kw1=class, ns1=std, pre1_t=expected, last=;)] [include(틀:C++ 요소, head_keyword=template, template_p0=FailureType, template_lnb=1, kw1=class, ns1=std, pre1_t=unexpected, last=;)]
C++23
에 추가된 expected<T, E>
는 성공한 사례에 대하여 T
를, 실패하면 E
를 반환하는 클래스다. expected<T, E>
의 생성자로 성공한 사례의 값을 전달하거나, unexpected<E>
를 써서 실패한 값을 전달할 수 있다.#!syntax cpp
struct [[nodiscard]] ApplyError_t
{
explicit ApplyError_t() noexcept = default;
};
우리는 성공하면 그대로 함수 반환값을 내보내고, 실패한 경우 이 구조체를 전달할 것이다.#!syntax cpp
import <type_traits>;
import <utility>;
import <expected>;
struct [[nodiscard]] ApplyError_t
{
explicit ApplyError_t() noexcept = default;
};
/*inline*/ constexpr ApplyError_t apply_error = {};
template<typename T, typename... Args>
auto TryApply(T&& obj, Args&&... args)
{
if constexpr (requires { (std::declval<T&&>()).operator()(std::declval<Arg&&>()...); })
{
using InvokeR = std::invoke_result_t<T&&, Args&&...>;
using R = std::expected<InvokeR, ApplyError_t>;
return static_cast<R>(std::invoke(std::forward<T>(obj), std::forward<Args>(args)...));
}
else
{
return std::unexpected{ apply_error };
}
}
상기 코드는 상수 조건문 에서 requires
식을 사용하고 있다. 개념을 외부에 선언하는 것은 이름공간이나 식별자 노출 문제로 의외로 순탄치 않을 때가 있다. 또한 메타 함수나 SFINAE 는 상수 표현식만 검사할 수 있다. 그럴 때는 즉석에서 제약조건을 만들어서 조건문으로 써먹을 수 있다. 제약조건의 명세와 활용례에 대해서 설명이 끝났다. 제약조건은 여러분이 쓰기 나름에 따라 활용성이 무궁무진하다. C++ 의 큰 결점 중 하나를 메꾼 혁신적인 기능으로써 디버깅과 코드 유지보수에 큰 도움이 된다. 가독성은 별로이지만.개념 라이브러리 C++20
에서 새로운 모듈이 개념과 함께 도입되었다. <concepts>
모듈은 이름있는 필요조건, 표준 라이브러리 구현 혹은 향후 필요할만한 사양도 갖추었다. 이 라이브러리는 문서에 내용을 다 담을 수 있을만큼 내용은 적지만 탑재된 개념은 한번씩 살펴볼만하다.||<tablewidth=80%><tablebordercolor=#20b580><rowbgcolor=#090f0a,#050b09><rowcolor=#d7d7d7,#a1a1a1>이름||설명||
<rowcolor=#090912,#bebebf>std::default_initializable
DefaultConstructible `T`
가 다음 조건 중 하나를 만족해야 함.원시 자료형 기본 초기화 를 할 수 있는 자료형.T{}
생성을 할 수 있는 자료형.T()
생성을 할 수 있는 자료형. std::convertible_to<From, To>
`From`
이 다음 조건을 모두 만족해야 함. std::destructible
Destructible `T`
가 다음 조건을 모두 만족해야 함.소멸자 를 호출할 수 있어야 함 .클래스 의 소멸자는 public
이어야 함.requires
식 때문에 모든 소멸자 후보가 삭제되면 안됨.소멸자가 예외를 던지면 안됨. 즉 noexcept(true)
이어야 함. std::constructible_from<T, Args...>
`T`
가 다음 조건을 모두 만족해야 함. std::move_constructible
MoveConstructible `T`
가 이동 생성 및 이동 변환이 가능해야 함. 정확히는 다음 조건을 모두 만족해야 함.constructible_from<T, T>
이 참임.convertible_to<T, T>
가 참임. std::copy_constructible
CopyConstructible `T`
가 복사 생성 및 복사 변환이 가능해야 함. 정확히는 다음 조건을 모두 만족해야 함.move_constructible
가 참임: 이동이 가능해야 함.constructible_from<T, T>
: T
를 받을 수 있는 생성자가 있어야 함.constructible_from<T, T&>
: T&
를 받을 수 있는 생성자가 있어야 함.constructible_from<T, const T&>
: const T&
를 받을 수 있는 생성자가 있어야 함.convertible_to<const T, T>
: const T
를 T
로 변환할 수 있어야 함.convertible_to<T&, T>
: T&
를 T
로 변환할 수 있어야 함.convertible_to<const T&, T>
: const T&
를 T
로 변환할 수 있어야 함. std::same_as<T, U>
`T`
와 `U`
가 참조자와 한정자 포함해서 똑같은 자료형이어야 함. 정확히는 다음 조건을 모두 만족해야 함.std::is_same_v<T, U>
가 참임.std::is_same_v<U, T>
가 참임. 이렇게 하는 이유는 `T`
와 `U`
둘중 하나가 함수 매개변수에 사용되면 완벽한 전달을 하지 않는 한 부패하므로 다른 자료형으로 보일 수 있기 때문임. 그렇기에 두번 검사해서 확실히 같은지 아닌지를 확인하는 것임. std::integral
`T`
가 정수 자료형이어야 함. 정확히는 다음 조건을 만족해야 함. std::signed_integral
`T`
가 부호 있는 정수 자료형이어야 함. 정확히는 다음 조건을 만족해야 함. std::unsigned_integral
`T`
가 부호 없는 정수 자료형이어야 함. 정확히는 다음 조건을 만족해야 함. std::floating_point
`T`
가 부동 소수점 자료형이어야 함. 정확히는 다음 조건을 만족해야 함. std::invocable<F, Args...>
`F`
와 `Args...`
를 완벽한 전달했을 때 ()
로 받아서 실행할 수 있는 자료형이어야 함. 가령 함수, 람다 표현식, 함자 등이 있음.requires(F&& f, Args&&... args)
{ std::invoke(std::forward(f), std::forward<Args>(args)...); }
std::regular_invocable<F, Args...>
invocable
과 거의 같지만 `F`
와 `Args...`
에 어떠한 조작을 하지말고 그대로 전달할 목적의 개념임. 그 결과 동등성 보존 을 달성하는데, 같은 종류의 자료형을 넣으면 항상 같은 결과가 나온다는 뜻임. 함수형 프로그래밍 참조.
===# 모듈 요약 코드 #===#!syntax cpp
namespace std
{
// language-related concepts
template<class T, class U>
concept same_as = is_same_v<T, U> && is_same_v<U, T>;
template<class Derived, class Base>
concept derived_from = is_base_of_v<Base, Derived> && is_convertible_v<const volatile Derived*, const volatile Base*>;
template<class From, class To>
concept convertible_to = is_convertible_v<From, To>
&& requires(From (&from)())
{
static_cast<To>(from()); // std::declval<From&>()
};
template<class T, class U>
concept common_reference_with = same_as<common_reference_t<T, U>, common_reference_t<U, T>>
&& convertible_to<T, common_reference_t<T, U>>
&& convertible_to<U, common_reference_t<T, U>>;
template<class T, class U>
concept common_with = same_as<common_type_t<T, U>, common_type_t<U, T>>
&& requires
{
static_cast<common_type_t<T, U>>(declval<T>());
static_cast<common_type_t<T, U>>(declval<U>());
}
&& common_reference_with<add_lvalue_reference_t<const T>, add_lvalue_reference_t<const U>>
&& common_reference_with<add_lvalue_reference_t<common_type_t<T, U>>, common_reference_t<add_lvalue_reference_t<const T>, add_lvalue_reference_t<const U>>>;
// arithmetic concepts
template<class T>
concept integral = is_integral_v<T>;
template<class T>
concept signed_integral = integral<T> && is_signed_v<T>;
template<class T>
concept unsigned_integral = integral<T> && !signed_integral<T>;
template<class T>
concept floating_point = is_floating_point_v<T>;
template<class LHS, class RHS>
concept assignable_from = is_lvalue_reference_v<LHS>
&& common_reference_with<const remove_reference_t<LHS>&, const remove_reference_t<RHS>&>
&& requires(LHS lhs, RHS&& rhs)
{
{ lhs = std::forward<RHS>(rhs) } -> same_as<LHS>;
}
namespace ranges
{
inline namespace /* unspecified */
{
inline constexpr /* unspecified */ swap = /* unspecified */;
}
}
template<class T>
concept swappable = requires(T& a, T& b) { ranges::swap(a, b); };
template<class T, class U>
concept swappable_with = common_reference_with<const remove_reference_t<T>&, const remove_reference_t<U>&>
&& requires(T&& t, U&& u)
{
ranges::swap(std::forward<T>(t), std::forward<T>(t));
ranges::swap(std::forward<U>(u), std::forward<U>(u));
ranges::swap(std::forward<T>(t), std::forward<U>(u));
ranges::swap(std::forward<U>(u), std::forward<T>(t));
};
template<class T>
concept destructible = is_nothrow_destructible_v<T>;
template<class T, class... Args>
concept constructible_from = destructible<T> && is_constructible_v<T, Args...>;
template<class T>
concept default_initializable = /* --- */;
template<class T>
concept move_constructible = constructible_from<T, T> && convertible_to<T, T>;
template<class T>
concept copy_constructible = move_constructible<T>
&& constructible_from<T, T&> && convertible_to<T&, T>
&& constructible_from<T, const T&> && convertible_to<const T&, T>
&& constructible_from<T, const T> && convertible_to<const T, T>;
// comparison concepts
template<class T, class U>
concept __WeaklyEqualityComparableWith = // exposition only
requires(const remove_reference_t<T>& t, const remove_reference_t<U>& u)
{
{ t == u } -> boolean-testable;
{ t != u } -> boolean-testable;
{ u == t } -> boolean-testable;
{ u != t } -> boolean-testable;
};
template<class T>
concept equality_comparable = __WeaklyEqualityComparableWith<T, T>;
template<class T>
concept equality_comparable = /* see description */;
template<class T, class U>
concept equality_comparable_with = equality_comparable<T> && equality_comparable<U>
&& common_reference_with<const remove_reference_t<T>&, const remove_reference_t<U>&>
&& equality_comparable<common_reference_t<const remove_reference_t<T>&, const remove_reference_t<U>&>>
&& __WeaklyEqualityComparableWith<T, U>;
template<class T>
concept totally_ordered = equality_comparable<T>
&& requires(const remove_reference_t<T>& a, const remove_reference_t<T>& b)
{
{ a < b } -> boolean-testable;
{ a > b } -> boolean-testable;
{ a <= b } -> boolean-testable;
{ a >= b } -> boolean-testable;
};
template<class T, class U>
concept totally_ordered_with = totally_ordered<T> && totally_ordered<U>
&& common_reference_with<const remove_reference_t<T>&, const remove_reference_t<U>&>
&& totally_ordered<common_reference_t<const remove_reference_t<T>&, const remove_reference_t<U>&>>
&& equality_comparable_with<T, U>
&& requires(const remove_reference_t<T>& t, const remove_reference_t<U>& u)
{
{ t < u } -> boolean-testable;
{ t > u } -> boolean-testable;
{ t <= u } -> boolean-testable;
{ t >= u } -> boolean-testable;
{ u < t } -> boolean-testable;
{ u > t } -> boolean-testable;
{ u <= t } -> boolean-testable;
{ u >= t } -> boolean-testable;
};
// object concepts
template<class T>
concept movable = is_object_v<T> && move_constructible<T>
&& assignable_from<T&, T> && swappable<T>;
template<class T>
concept copyable = copy_constructible<T> && movable<T> && assignable_from<T&, T&>
&& assignable_from<T&, const T&> && assignable_from<T&, const T>;
template<class T>
concept semiregular = copyable<T> && default_initializable<T>;
template<class T>
concept regular = semiregular<T> && equality_comparable<T>;
// callable concepts
template<class F, class... Args>
concept invocable = requires(F&& f, Args&&... args)
{
std::invoke(std::forward<F>(f), std::forward<Args>(args)...);
};
template<class F, class... Args>
concept regular_invocable = invocable<F, Args...>;
template<class F, class... Args>
concept predicate = regular_invocable<F, Args...> && boolean-testable<invoke_result_t<F, Args...>>;
template<class R, class T, class U>
concept relation = predicate<R, T, T> && predicate<R, U, U> && predicate<R, T, U> && predicate<R, U, T>;
template<class R, class T, class U>
concept equivalence_relation = relation<R, T, U>;
template<class R, class T, class U>
concept strict_weak_order = relation<R, T, U>;
}
각각 구현을 살펴보면서 어떻게 동작하는지 알아보는 것도 좋다.
#!if version2 == null
{{{#!wiki style="border:1px solid gray;border-top:5px solid gray;padding:7px;margin-bottom:0px"
[[크리에이티브 커먼즈 라이선스|[[파일:CC-white.svg|width=22.5px]]]] 이 문서의 내용 중 전체 또는 일부는 {{{#!wiki style="display: inline-block"
{{{#!if external == "o"
[[https://namu.wiki/w/C++/표준 라이브러리/concepts|C++/표준 라이브러리/concepts]]}}}{{{#!if external != "o"
[[C++/표준 라이브러리/concepts]]}}}}}} 문서의 {{{#!if uuid == null
'''uuid not found'''}}}{{{#!if uuid != null
[[https://namu.wiki/w/C++/표준 라이브러리/concepts?uuid=ff01d756-b991-4523-9492-8b19ac964a20|r9]]}}} 판{{{#!if paragraph != null
, [[https://namu.wiki/w/C++/표준 라이브러리/concepts?uuid=ff01d756-b991-4523-9492-8b19ac964a20#s-|번 문단]]}}}에서 가져왔습니다. [[https://namu.wiki/history/C++/표준 라이브러리/concepts?from=9|이전 역사 보러 가기]]}}}
#!if version2 != null
{{{#!wiki style="display: block;"
{{{#!wiki style="border:1px solid gray;border-top:5px solid gray;padding:7px;margin-bottom:0px"
[[크리에이티브 커먼즈 라이선스|[[파일:CC-white.svg|width=22.5px]]]] 이 문서의 내용 중 전체 또는 일부는 다른 문서에서 가져왔습니다.
{{{#!wiki style="text-align: center"
{{{#!folding [ 펼치기 · 접기 ]
{{{#!wiki style="text-align: left; padding: 0px 10px"
{{{#!wiki style="display: inline-block"
{{{#!if external == "o"
[[https://namu.wiki/w/C++/표준 라이브러리/concepts|C++/표준 라이브러리/concepts]]}}}{{{#!if external != "o"
[[C++/표준 라이브러리/concepts]]}}}}}} 문서의 {{{#!if uuid == null
'''uuid not found'''}}}{{{#!if uuid != null
[[https://namu.wiki/w/C++/표준 라이브러리/concepts?uuid=ff01d756-b991-4523-9492-8b19ac964a20|r9]]}}} 판{{{#!if paragraph != null
, [[https://namu.wiki/w/C++/표준 라이브러리/concepts?uuid=ff01d756-b991-4523-9492-8b19ac964a20#s-|번 문단]]}}} ([[https://namu.wiki/history/C++/표준 라이브러리/concepts?from=9|이전 역사]])
{{{#!wiki style="display: inline-block"
{{{#!if external == "o"
[[https://namu.wiki/w/|]]}}}{{{#!if external != "o"
[[]]}}}}}} 문서의 {{{#!if uuid2 == null
'''uuid2 not found'''}}}{{{#!if uuid2 != null
[[https://namu.wiki/w/?uuid=|r]]}}} 판{{{#!if paragraph2 != null
, [[https://namu.wiki/w/?uuid=#s-|번 문단]]}}} ([[https://namu.wiki/history/?from=|이전 역사]]){{{#!if version3 != null
{{{#!wiki style="display: block;"
{{{#!wiki style="display: inline-block"
{{{#!if external == "o"
[[https://namu.wiki/w/|]]}}}{{{#!if external != "o"
[[]]}}}}}} 문서의 {{{#!if uuid3 == null
'''uuid3 not found'''}}}{{{#!if uuid3 != null
[[https://namu.wiki/w/?uuid=|r]]}}} 판{{{#!if paragraph3 != null
, [[https://namu.wiki/w/?uuid=#s-|번 문단]]}}} ([[https://namu.wiki/history/?from=|이전 역사]])}}}}}}{{{#!if version4 != null
{{{#!wiki style="display: block;"
{{{#!wiki style="display: inline-block"
{{{#!if external == "o"
[[https://namu.wiki/w/|]]}}}{{{#!if external != "o"
[[]]}}}}}} 문서의 {{{#!if uuid4 == null
'''uuid4 not found'''}}}{{{#!if uuid4 != null
[[https://namu.wiki/w/?uuid=|r]]}}} 판{{{#!if paragraph4 != null
, [[https://namu.wiki/w/?uuid=#s-|번 문단]]}}} ([[https://namu.wiki/history/?from=|이전 역사]])}}}}}}{{{#!if version5 != null
{{{#!wiki style="display: block;"
{{{#!wiki style="display: inline-block"
{{{#!if external == "o"
[[https://namu.wiki/w/|]]}}}{{{#!if external != "o"
[[]]}}}}}} 문서의 {{{#!if uuid5 == null
'''uuid5 not found'''}}}{{{#!if uuid5 != null
[[https://namu.wiki/w/?uuid=|r]]}}} 판{{{#!if paragraph5 != null
, [[https://namu.wiki/w/?uuid=#s-|번 문단]]}}} ([[https://namu.wiki/history/?from=|이전 역사]])}}}}}}{{{#!if version6 != null
{{{#!wiki style="display: block;"
{{{#!wiki style="display: inline-block"
{{{#!if external == "o"
[[https://namu.wiki/w/|]]}}}{{{#!if external != "o"
[[]]}}}}}} 문서의 {{{#!if uuid6 == null
'''uuid6 not found'''}}}{{{#!if uuid6 != null
[[https://namu.wiki/w/?uuid=|r]]}}} 판{{{#!if paragraph6 != null
, [[https://namu.wiki/w/?uuid=#s-|번 문단]]}}} ([[https://namu.wiki/history/?from=|이전 역사]])}}}}}}{{{#!if version7 != null
{{{#!wiki style="display: block;"
{{{#!wiki style="display: inline-block"
{{{#!if external == "o"
[[https://namu.wiki/w/|]]}}}{{{#!if external != "o"
[[]]}}}}}} 문서의 {{{#!if uuid7 == null
'''uuid7 not found'''}}}{{{#!if uuid7 != null
[[https://namu.wiki/w/?uuid=|r]]}}} 판{{{#!if paragraph7 != null
, [[https://namu.wiki/w/?uuid=#s-|번 문단]]}}} ([[https://namu.wiki/history/?from=|이전 역사]])}}}}}}{{{#!if version8 != null
{{{#!wiki style="display: block;"
{{{#!wiki style="display: inline-block"
{{{#!if external == "o"
[[https://namu.wiki/w/|]]}}}{{{#!if external != "o"
[[]]}}}}}} 문서의 {{{#!if uuid8 == null
'''uuid8 not found'''}}}{{{#!if uuid8 != null
[[https://namu.wiki/w/?uuid=|r]]}}} 판{{{#!if paragraph8 != null
, [[https://namu.wiki/w/?uuid=#s-|번 문단]]}}} ([[https://namu.wiki/history/?from=|이전 역사]])}}}}}}{{{#!if version9 != null
{{{#!wiki style="display: block;"
{{{#!wiki style="display: inline-block"
{{{#!if external == "o"
[[https://namu.wiki/w/|]]}}}{{{#!if external != "o"
[[]]}}}}}} 문서의 {{{#!if uuid9 == null
'''uuid9 not found'''}}}{{{#!if uuid9 != null
[[https://namu.wiki/w/?uuid=|r]]}}} 판{{{#!if paragraph9 != null
, [[https://namu.wiki/w/?uuid=#s-|번 문단]]}}} ([[https://namu.wiki/history/?from=|이전 역사]])}}}}}}{{{#!if version10 != null
{{{#!wiki style="display: block;"
{{{#!wiki style="display: inline-block"
{{{#!if external == "o"
[[https://namu.wiki/w/|]]}}}{{{#!if external != "o"
[[]]}}}}}} 문서의 {{{#!if uuid10 == null
'''uuid10 not found'''}}}{{{#!if uuid10 != null
[[https://namu.wiki/w/?uuid=|r]]}}} 판{{{#!if paragraph10 != null
, [[https://namu.wiki/w/?uuid=#s-|번 문단]]}}} ([[https://namu.wiki/history/?from=|이전 역사]])}}}}}}{{{#!if version11 != null
{{{#!wiki style="display: block;"
{{{#!wiki style="display: inline-block"
{{{#!if external == "o"
[[https://namu.wiki/w/|]]}}}{{{#!if external != "o"
[[]]}}}}}} 문서의 {{{#!if uuid11 == null
'''uuid11 not found'''}}}{{{#!if uuid11 != null
[[https://namu.wiki/w/?uuid=|r]]}}} 판{{{#!if paragraph11 != null
, [[https://namu.wiki/w/?uuid=#s-|번 문단]]}}} ([[https://namu.wiki/history/?from=|이전 역사]])}}}}}}{{{#!if version12 != null
{{{#!wiki style="display: block;"
{{{#!wiki style="display: inline-block"
{{{#!if external == "o"
[[https://namu.wiki/w/|]]}}}{{{#!if external != "o"
[[]]}}}}}} 문서의 {{{#!if uuid12 == null
'''uuid12 not found'''}}}{{{#!if uuid12 != null
[[https://namu.wiki/w/?uuid=|r]]}}} 판{{{#!if paragraph12 != null
, [[https://namu.wiki/w/?uuid=#s-|번 문단]]}}} ([[https://namu.wiki/history/?from=|이전 역사]])}}}}}}{{{#!if version13 != null
{{{#!wiki style="display: block;"
{{{#!wiki style="display: inline-block"
{{{#!if external == "o"
[[https://namu.wiki/w/|]]}}}{{{#!if external != "o"
[[]]}}}}}} 문서의 {{{#!if uuid13 == null
'''uuid13 not found'''}}}{{{#!if uuid13 != null
[[https://namu.wiki/w/?uuid=|r]]}}} 판{{{#!if paragraph13 != null
, [[https://namu.wiki/w/?uuid=#s-|번 문단]]}}} ([[https://namu.wiki/history/?from=|이전 역사]])}}}}}}{{{#!if version14 != null
{{{#!wiki style="display: block;"
{{{#!wiki style="display: inline-block"
{{{#!if external == "o"
[[https://namu.wiki/w/|]]}}}{{{#!if external != "o"
[[]]}}}}}} 문서의 {{{#!if uuid14 == null
'''uuid14 not found'''}}}{{{#!if uuid14 != null
[[https://namu.wiki/w/?uuid=|r]]}}} 판{{{#!if paragraph14 != null
, [[https://namu.wiki/w/?uuid=#s-|번 문단]]}}} ([[https://namu.wiki/history/?from=|이전 역사]])}}}}}}{{{#!if version15 != null
{{{#!wiki style="display: block;"
{{{#!wiki style="display: inline-block"
{{{#!if external == "o"
[[https://namu.wiki/w/|]]}}}{{{#!if external != "o"
[[]]}}}}}} 문서의 {{{#!if uuid15 == null
'''uuid15 not found'''}}}{{{#!if uuid15 != null
[[https://namu.wiki/w/?uuid=|r]]}}} 판{{{#!if paragraph15 != null
, [[https://namu.wiki/w/?uuid=#s-|번 문단]]}}} ([[https://namu.wiki/history/?from=|이전 역사]])}}}}}}{{{#!if version16 != null
{{{#!wiki style="display: block;"
{{{#!wiki style="display: inline-block"
{{{#!if external == "o"
[[https://namu.wiki/w/|]]}}}{{{#!if external != "o"
[[]]}}}}}} 문서의 {{{#!if uuid16 == null
'''uuid16 not found'''}}}{{{#!if uuid16 != null
[[https://namu.wiki/w/?uuid=|r]]}}} 판{{{#!if paragraph16 != null
, [[https://namu.wiki/w/?uuid=#s-|번 문단]]}}} ([[https://namu.wiki/history/?from=|이전 역사]])}}}}}}{{{#!if version17 != null
{{{#!wiki style="display: block;"
{{{#!wiki style="display: inline-block"
{{{#!if external == "o"
[[https://namu.wiki/w/|]]}}}{{{#!if external != "o"
[[]]}}}}}} 문서의 {{{#!if uuid17 == null
'''uuid17 not found'''}}}{{{#!if uuid17 != null
[[https://namu.wiki/w/?uuid=|r]]}}} 판{{{#!if paragraph17 != null
, [[https://namu.wiki/w/?uuid=#s-|번 문단]]}}} ([[https://namu.wiki/history/?from=|이전 역사]])}}}}}}{{{#!if version18 != null
{{{#!wiki style="display: block;"
{{{#!wiki style="display: inline-block"
{{{#!if external == "o"
[[https://namu.wiki/w/|]]}}}{{{#!if external != "o"
[[]]}}}}}} 문서의 {{{#!if uuid18 == null
'''uuid18 not found'''}}}{{{#!if uuid18 != null
[[https://namu.wiki/w/?uuid=|r]]}}} 판{{{#!if paragraph18 != null
, [[https://namu.wiki/w/?uuid=#s-|번 문단]]}}} ([[https://namu.wiki/history/?from=|이전 역사]])}}}}}}{{{#!if version19 != null
{{{#!wiki style="display: block;"
{{{#!wiki style="display: inline-block"
{{{#!if external == "o"
[[https://namu.wiki/w/|]]}}}{{{#!if external != "o"
[[]]}}}}}} 문서의 {{{#!if uuid19 == null
'''uuid19 not found'''}}}{{{#!if uuid19 != null
[[https://namu.wiki/w/?uuid=|r]]}}} 판{{{#!if paragraph19 != null
, [[https://namu.wiki/w/?uuid=#s-|번 문단]]}}} ([[https://namu.wiki/history/?from=|이전 역사]])}}}}}}{{{#!if version20 != null
{{{#!wiki style="display: block;"
{{{#!wiki style="display: inline-block"
{{{#!if external == "o"
[[https://namu.wiki/w/|]]}}}{{{#!if external != "o"
[[]]}}}}}} 문서의 {{{#!if uuid20 == null
'''uuid20 not found'''}}}{{{#!if uuid20 != null
[[https://namu.wiki/w/?uuid=|r]]}}} 판{{{#!if paragraph20 != null
, [[https://namu.wiki/w/?uuid=#s-|번 문단]]}}} ([[https://namu.wiki/history/?from=|이전 역사]])}}}}}}{{{#!if version21 != null
{{{#!wiki style="display: block;"
{{{#!wiki style="display: inline-block"
{{{#!if external == "o"
[[https://namu.wiki/w/|]]}}}{{{#!if external != "o"
[[]]}}}}}} 문서의 {{{#!if uuid21 == null
'''uuid21 not found'''}}}{{{#!if uuid21 != null
[[https://namu.wiki/w/?uuid=|r]]}}} 판{{{#!if paragraph21 != null
, [[https://namu.wiki/w/?uuid=#s-|번 문단]]}}} ([[https://namu.wiki/history/?from=|이전 역사]])}}}}}}{{{#!if version22 != null
{{{#!wiki style="display: block;"
{{{#!wiki style="display: inline-block"
{{{#!if external == "o"
[[https://namu.wiki/w/|]]}}}{{{#!if external != "o"
[[]]}}}}}} 문서의 {{{#!if uuid22 == null
'''uuid22 not found'''}}}{{{#!if uuid22 != null
[[https://namu.wiki/w/?uuid=|r]]}}} 판{{{#!if paragraph22 != null
, [[https://namu.wiki/w/?uuid=#s-|번 문단]]}}} ([[https://namu.wiki/history/?from=|이전 역사]])}}}}}}{{{#!if version23 != null
{{{#!wiki style="display: block;"
{{{#!wiki style="display: inline-block"
{{{#!if external == "o"
[[https://namu.wiki/w/|]]}}}{{{#!if external != "o"
[[]]}}}}}} 문서의 {{{#!if uuid23 == null
'''uuid23 not found'''}}}{{{#!if uuid23 != null
[[https://namu.wiki/w/?uuid=|r]]}}} 판{{{#!if paragraph23 != null
, [[https://namu.wiki/w/?uuid=#s-|번 문단]]}}} ([[https://namu.wiki/history/?from=|이전 역사]])}}}}}}{{{#!if version24 != null
{{{#!wiki style="display: block;"
{{{#!wiki style="display: inline-block"
{{{#!if external == "o"
[[https://namu.wiki/w/|]]}}}{{{#!if external != "o"
[[]]}}}}}} 문서의 {{{#!if uuid24 == null
'''uuid24 not found'''}}}{{{#!if uuid24 != null
[[https://namu.wiki/w/?uuid=|r]]}}} 판{{{#!if paragraph24 != null
, [[https://namu.wiki/w/?uuid=#s-|번 문단]]}}} ([[https://namu.wiki/history/?from=|이전 역사]])}}}}}}{{{#!if version25 != null
{{{#!wiki style="display: block;"
{{{#!wiki style="display: inline-block"
{{{#!if external == "o"
[[https://namu.wiki/w/|]]}}}{{{#!if external != "o"
[[]]}}}}}} 문서의 {{{#!if uuid25 == null
'''uuid25 not found'''}}}{{{#!if uuid25 != null
[[https://namu.wiki/w/?uuid=|r]]}}} 판{{{#!if paragraph25 != null
, [[https://namu.wiki/w/?uuid=#s-|번 문단]]}}} ([[https://namu.wiki/history/?from=|이전 역사]])}}}}}}{{{#!if version26 != null
{{{#!wiki style="display: block;"
{{{#!wiki style="display: inline-block"
{{{#!if external == "o"
[[https://namu.wiki/w/|]]}}}{{{#!if external != "o"
[[]]}}}}}} 문서의 {{{#!if uuid26 == null
'''uuid26 not found'''}}}{{{#!if uuid26 != null
[[https://namu.wiki/w/?uuid=|r]]}}} 판{{{#!if paragraph26 != null
, [[https://namu.wiki/w/?uuid=#s-|번 문단]]}}} ([[https://namu.wiki/history/?from=|이전 역사]])}}}}}}{{{#!if version27 != null
{{{#!wiki style="display: block;"
{{{#!wiki style="display: inline-block"
{{{#!if external == "o"
[[https://namu.wiki/w/|]]}}}{{{#!if external != "o"
[[]]}}}}}} 문서의 {{{#!if uuid27 == null
'''uuid27 not found'''}}}{{{#!if uuid27 != null
[[https://namu.wiki/w/?uuid=|r]]}}} 판{{{#!if paragraph27 != null
, [[https://namu.wiki/w/?uuid=#s-|번 문단]]}}} ([[https://namu.wiki/history/?from=|이전 역사]])}}}}}}{{{#!if version28 != null
{{{#!wiki style="display: block;"
{{{#!wiki style="display: inline-block"
{{{#!if external == "o"
[[https://namu.wiki/w/|]]}}}{{{#!if external != "o"
[[]]}}}}}} 문서의 {{{#!if uuid28 == null
'''uuid28 not found'''}}}{{{#!if uuid28 != null
[[https://namu.wiki/w/?uuid=|r]]}}} 판{{{#!if paragraph28 != null
, [[https://namu.wiki/w/?uuid=#s-|번 문단]]}}} ([[https://namu.wiki/history/?from=|이전 역사]])}}}}}}{{{#!if version29 != null
{{{#!wiki style="display: block;"
{{{#!wiki style="display: inline-block"
{{{#!if external == "o"
[[https://namu.wiki/w/|]]}}}{{{#!if external != "o"
[[]]}}}}}} 문서의 {{{#!if uuid29 == null
'''uuid29 not found'''}}}{{{#!if uuid29 != null
[[https://namu.wiki/w/?uuid=|r]]}}} 판{{{#!if paragraph29 != null
, [[https://namu.wiki/w/?uuid=#s-|번 문단]]}}} ([[https://namu.wiki/history/?from=|이전 역사]])}}}}}}{{{#!if version30 != null
{{{#!wiki style="display: block;"
{{{#!wiki style="display: inline-block"
{{{#!if external == "o"
[[https://namu.wiki/w/|]]}}}{{{#!if external != "o"
[[]]}}}}}} 문서의 {{{#!if uuid30 == null
'''uuid30 not found'''}}}{{{#!if uuid30 != null
[[https://namu.wiki/w/?uuid=|r]]}}} 판{{{#!if paragraph30 != null
, [[https://namu.wiki/w/?uuid=#s-|번 문단]]}}} ([[https://namu.wiki/history/?from=|이전 역사]])}}}}}}{{{#!if version31 != null
{{{#!wiki style="display: block;"
{{{#!wiki style="display: inline-block"
{{{#!if external == "o"
[[https://namu.wiki/w/|]]}}}{{{#!if external != "o"
[[]]}}}}}} 문서의 {{{#!if uuid31 == null
'''uuid31 not found'''}}}{{{#!if uuid31 != null
[[https://namu.wiki/w/?uuid=|r]]}}} 판{{{#!if paragraph31 != null
, [[https://namu.wiki/w/?uuid=#s-|번 문단]]}}} ([[https://namu.wiki/history/?from=|이전 역사]])}}}}}}{{{#!if version32 != null
{{{#!wiki style="display: block;"
{{{#!wiki style="display: inline-block"
{{{#!if external == "o"
[[https://namu.wiki/w/|]]}}}{{{#!if external != "o"
[[]]}}}}}} 문서의 {{{#!if uuid32 == null
'''uuid32 not found'''}}}{{{#!if uuid32 != null
[[https://namu.wiki/w/?uuid=|r]]}}} 판{{{#!if paragraph32 != null
, [[https://namu.wiki/w/?uuid=#s-|번 문단]]}}} ([[https://namu.wiki/history/?from=|이전 역사]])}}}}}}{{{#!if version33 != null
{{{#!wiki style="display: block;"
{{{#!wiki style="display: inline-block"
{{{#!if external == "o"
[[https://namu.wiki/w/|]]}}}{{{#!if external != "o"
[[]]}}}}}} 문서의 {{{#!if uuid33 == null
'''uuid33 not found'''}}}{{{#!if uuid33 != null
[[https://namu.wiki/w/?uuid=|r]]}}} 판{{{#!if paragraph33 != null
, [[https://namu.wiki/w/?uuid=#s-|번 문단]]}}} ([[https://namu.wiki/history/?from=|이전 역사]])}}}}}}{{{#!if version34 != null
{{{#!wiki style="display: block;"
{{{#!wiki style="display: inline-block"
{{{#!if external == "o"
[[https://namu.wiki/w/|]]}}}{{{#!if external != "o"
[[]]}}}}}} 문서의 {{{#!if uuid34 == null
'''uuid34 not found'''}}}{{{#!if uuid34 != null
[[https://namu.wiki/w/?uuid=|r]]}}} 판{{{#!if paragraph34 != null
, [[https://namu.wiki/w/?uuid=#s-|번 문단]]}}} ([[https://namu.wiki/history/?from=|이전 역사]])}}}}}}{{{#!if version35 != null
{{{#!wiki style="display: block;"
{{{#!wiki style="display: inline-block"
{{{#!if external == "o"
[[https://namu.wiki/w/|]]}}}{{{#!if external != "o"
[[]]}}}}}} 문서의 {{{#!if uuid35 == null
'''uuid35 not found'''}}}{{{#!if uuid35 != null
[[https://namu.wiki/w/?uuid=|r]]}}} 판{{{#!if paragraph35 != null
, [[https://namu.wiki/w/?uuid=#s-|번 문단]]}}} ([[https://namu.wiki/history/?from=|이전 역사]])}}}}}}{{{#!if version36 != null
{{{#!wiki style="display: block;"
{{{#!wiki style="display: inline-block"
{{{#!if external == "o"
[[https://namu.wiki/w/|]]}}}{{{#!if external != "o"
[[]]}}}}}} 문서의 {{{#!if uuid36 == null
'''uuid36 not found'''}}}{{{#!if uuid36 != null
[[https://namu.wiki/w/?uuid=|r]]}}} 판{{{#!if paragraph36 != null
, [[https://namu.wiki/w/?uuid=#s-|번 문단]]}}} ([[https://namu.wiki/history/?from=|이전 역사]])}}}}}}{{{#!if version37 != null
{{{#!wiki style="display: block;"
{{{#!wiki style="display: inline-block"
{{{#!if external == "o"
[[https://namu.wiki/w/|]]}}}{{{#!if external != "o"
[[]]}}}}}} 문서의 {{{#!if uuid37 == null
'''uuid37 not found'''}}}{{{#!if uuid37 != null
[[https://namu.wiki/w/?uuid=|r]]}}} 판{{{#!if paragraph37 != null
, [[https://namu.wiki/w/?uuid=#s-|번 문단]]}}} ([[https://namu.wiki/history/?from=|이전 역사]])}}}}}}{{{#!if version38 != null
{{{#!wiki style="display: block;"
{{{#!wiki style="display: inline-block"
{{{#!if external == "o"
[[https://namu.wiki/w/|]]}}}{{{#!if external != "o"
[[]]}}}}}} 문서의 {{{#!if uuid38 == null
'''uuid38 not found'''}}}{{{#!if uuid38 != null
[[https://namu.wiki/w/?uuid=|r]]}}} 판{{{#!if paragraph38 != null
, [[https://namu.wiki/w/?uuid=#s-|번 문단]]}}} ([[https://namu.wiki/history/?from=|이전 역사]])}}}}}}{{{#!if version39 != null
{{{#!wiki style="display: block;"
{{{#!wiki style="display: inline-block"
{{{#!if external == "o"
[[https://namu.wiki/w/|]]}}}{{{#!if external != "o"
[[]]}}}}}} 문서의 {{{#!if uuid39 == null
'''uuid39 not found'''}}}{{{#!if uuid39 != null
[[https://namu.wiki/w/?uuid=|r]]}}} 판{{{#!if paragraph39 != null
, [[https://namu.wiki/w/?uuid=#s-|번 문단]]}}} ([[https://namu.wiki/history/?from=|이전 역사]])}}}}}}{{{#!if version40 != null
{{{#!wiki style="display: block;"
{{{#!wiki style="display: inline-block"
{{{#!if external == "o"
[[https://namu.wiki/w/|]]}}}{{{#!if external != "o"
[[]]}}}}}} 문서의 {{{#!if uuid40 == null
'''uuid40 not found'''}}}{{{#!if uuid40 != null
[[https://namu.wiki/w/?uuid=|r]]}}} 판{{{#!if paragraph40 != null
, [[https://namu.wiki/w/?uuid=#s-|번 문단]]}}} ([[https://namu.wiki/history/?from=|이전 역사]])}}}}}}{{{#!if version41 != null
{{{#!wiki style="display: block;"
{{{#!wiki style="display: inline-block"
{{{#!if external == "o"
[[https://namu.wiki/w/|]]}}}{{{#!if external != "o"
[[]]}}}}}} 문서의 {{{#!if uuid41 == null
'''uuid41 not found'''}}}{{{#!if uuid41 != null
[[https://namu.wiki/w/?uuid=|r]]}}} 판{{{#!if paragraph41 != null
, [[https://namu.wiki/w/?uuid=#s-|번 문단]]}}} ([[https://namu.wiki/history/?from=|이전 역사]])}}}}}}{{{#!if version42 != null
{{{#!wiki style="display: block;"
{{{#!wiki style="display: inline-block"
{{{#!if external == "o"
[[https://namu.wiki/w/|]]}}}{{{#!if external != "o"
[[]]}}}}}} 문서의 {{{#!if uuid42 == null
'''uuid42 not found'''}}}{{{#!if uuid42 != null
[[https://namu.wiki/w/?uuid=|r]]}}} 판{{{#!if paragraph42 != null
, [[https://namu.wiki/w/?uuid=#s-|번 문단]]}}} ([[https://namu.wiki/history/?from=|이전 역사]])}}}}}}{{{#!if version43 != null
{{{#!wiki style="display: block;"
{{{#!wiki style="display: inline-block"
{{{#!if external == "o"
[[https://namu.wiki/w/|]]}}}{{{#!if external != "o"
[[]]}}}}}} 문서의 {{{#!if uuid43 == null
'''uuid43 not found'''}}}{{{#!if uuid43 != null
[[https://namu.wiki/w/?uuid=|r]]}}} 판{{{#!if paragraph43 != null
, [[https://namu.wiki/w/?uuid=#s-|번 문단]]}}} ([[https://namu.wiki/history/?from=|이전 역사]])}}}}}}{{{#!if version44 != null
{{{#!wiki style="display: block;"
{{{#!wiki style="display: inline-block"
{{{#!if external == "o"
[[https://namu.wiki/w/|]]}}}{{{#!if external != "o"
[[]]}}}}}} 문서의 {{{#!if uuid44 == null
'''uuid44 not found'''}}}{{{#!if uuid44 != null
[[https://namu.wiki/w/?uuid=|r]]}}} 판{{{#!if paragraph44 != null
, [[https://namu.wiki/w/?uuid=#s-|번 문단]]}}} ([[https://namu.wiki/history/?from=|이전 역사]])}}}}}}{{{#!if version45 != null
{{{#!wiki style="display: block;"
{{{#!wiki style="display: inline-block"
{{{#!if external == "o"
[[https://namu.wiki/w/|]]}}}{{{#!if external != "o"
[[]]}}}}}} 문서의 {{{#!if uuid45 == null
'''uuid45 not found'''}}}{{{#!if uuid45 != null
[[https://namu.wiki/w/?uuid=|r]]}}} 판{{{#!if paragraph45 != null
, [[https://namu.wiki/w/?uuid=#s-|번 문단]]}}} ([[https://namu.wiki/history/?from=|이전 역사]])}}}}}}{{{#!if version46 != null
{{{#!wiki style="display: block;"
{{{#!wiki style="display: inline-block"
{{{#!if external == "o"
[[https://namu.wiki/w/|]]}}}{{{#!if external != "o"
[[]]}}}}}} 문서의 {{{#!if uuid46 == null
'''uuid46 not found'''}}}{{{#!if uuid46 != null
[[https://namu.wiki/w/?uuid=|r]]}}} 판{{{#!if paragraph46 != null
, [[https://namu.wiki/w/?uuid=#s-|번 문단]]}}} ([[https://namu.wiki/history/?from=|이전 역사]])}}}}}}{{{#!if version47 != null
{{{#!wiki style="display: block;"
{{{#!wiki style="display: inline-block"
{{{#!if external == "o"
[[https://namu.wiki/w/|]]}}}{{{#!if external != "o"
[[]]}}}}}} 문서의 {{{#!if uuid47 == null
'''uuid47 not found'''}}}{{{#!if uuid47 != null
[[https://namu.wiki/w/?uuid=|r]]}}} 판{{{#!if paragraph47 != null
, [[https://namu.wiki/w/?uuid=#s-|번 문단]]}}} ([[https://namu.wiki/history/?from=|이전 역사]])}}}}}}{{{#!if version48 != null
{{{#!wiki style="display: block;"
{{{#!wiki style="display: inline-block"
{{{#!if external == "o"
[[https://namu.wiki/w/|]]}}}{{{#!if external != "o"
[[]]}}}}}} 문서의 {{{#!if uuid48 == null
'''uuid48 not found'''}}}{{{#!if uuid48 != null
[[https://namu.wiki/w/?uuid=|r]]}}} 판{{{#!if paragraph48 != null
, [[https://namu.wiki/w/?uuid=#s-|번 문단]]}}} ([[https://namu.wiki/history/?from=|이전 역사]])}}}}}}{{{#!if version49 != null
{{{#!wiki style="display: block;"
{{{#!wiki style="display: inline-block"
{{{#!if external == "o"
[[https://namu.wiki/w/|]]}}}{{{#!if external != "o"
[[]]}}}}}} 문서의 {{{#!if uuid49 == null
'''uuid49 not found'''}}}{{{#!if uuid49 != null
[[https://namu.wiki/w/?uuid=|r]]}}} 판{{{#!if paragraph49 != null
, [[https://namu.wiki/w/?uuid=#s-|번 문단]]}}} ([[https://namu.wiki/history/?from=|이전 역사]])}}}}}}{{{#!if version50 != null
{{{#!wiki style="display: block;"
{{{#!wiki style="display: inline-block"
{{{#!if external == "o"
[[https://namu.wiki/w/|]]}}}{{{#!if external != "o"
[[]]}}}}}} 문서의 {{{#!if uuid50 == null
'''uuid50 not found'''}}}{{{#!if uuid50 != null
[[https://namu.wiki/w/?uuid=|r]]}}} 판{{{#!if paragraph50 != null
, [[https://namu.wiki/w/?uuid=#s-|번 문단]]}}} ([[https://namu.wiki/history/?from=|이전 역사]])}}}}}}}}}}}}}}}}}}}}}