requires
expression
(since C++20)
제약 조건을 설명하는 bool 타입의 prvalue 표현식을 생성합니다.
목차 |
구문
requires
{
requirement-seq
}
|
(1) | ||||||||
requires
(
parameter-list
(선택 사항)
)
{
requirement-seq
}
|
(2) | ||||||||
| parameter-list | - | a parameter list |
| requirement-seq | - | sequence of requirements , each requirement is one of the following: |
설명
요구 사항은 현재 범위 내에 있는 템플릿 매개변수, parameter-list 의 매개변수, 그리고 둘러싸는 컨텍스트에서 보이는 다른 선언들을 참조할 수 있습니다.
템플릿 인수를 템플릿화된 엔티티 의 선언에서 사용된 requires 표현식에 대입할 경우, 해당 요구 사항에서 유효하지 않은 타입이나 표현식이 생성되거나 해당 요구 사항의 의미론적 제약 조건을 위반할 수 있습니다. 이러한 경우 requires 표현식은 false 로 평가되며 프로그램이 비정형이 되지 않습니다. 대입 및 의미론적 제약 조건 검사는 어휘 순서로 진행되며 requires 표현식의 결과를 결정하는 조건이 발견되면 중단됩니다. 대입(있는 경우)과 의미론적 제약 조건 검사가 성공하면 requires 표현식은 true 로 평가됩니다.
모든 가능한 템플릿 인자에 대해 requires 표현식에서 치환 실패가 발생한다면, 프로그램은 형식 오류이며 진단이 필요하지 않습니다:
template<class T> concept C = requires { new int[-(int)sizeof(T)]; // 모든 T에 대해 유효하지 않음: 형식이 잘못됨, 진단이 필요하지 않음 };
만약 requires 표현식이 해당 요구 사항에 유효하지 않은 타입이나 표현식을 포함하고, 템플릿화된 엔티티 의 선언 내에 나타나지 않는 경우, 프로그램은 형식에 맞지 않습니다.
로컬 매개변수
A requires 표현식은 매개변수 목록 을 사용하여 지역 매개변수를 도입할 수 있습니다. 이러한 매개변수는 연결성, 저장 공간, 수명을 가지지 않으며 요구 사항을 정의하기 위한 표기법으로만 사용됩니다.
각 매개변수의 타입은 함수 매개변수의 실제 타입을 결정하는 방식과 동일하게 결정됩니다:
template<typename T> concept C = requires(T p[2]) { (decltype(p))nullptr; // OK, p는 T* 타입을 가짐 };
다음 조건 중 하나라도 충족되면 프로그램의 형식이 잘못되었습니다:
- 로컬 매개변수가 default argument 를 갖습니다.
- 매개변수 목록이 생략부호로 종료됩니다.
template<typename T> concept C1 = requires(T t = 0) // 오류: t에 기본 인수가 있음 { t; }; template<typename T> concept C2 = requires(T t, ...) // 오류: 생략부호로 종료됨 { t; };
단순 요구사항
표현식
;
|
|||||||||
| expression | - | requires 로 시작하지 않는 표현식 |
단순 요구 사항은
expression
이 유효함을 주장합니다.
expression
은
unevaluated operand
입니다.
template<typename T> concept Addable = requires (T a, T b) { a + b; // "표현식 'a + b'가 컴파일될 수 있는 유효한 표현식이다" }; template<class T, class U = T> concept Swappable = requires(T&& t, U&& u) { swap(std::forward<T>(t), std::forward<U>(u)); swap(std::forward<U>(u), std::forward<T>(t)); };
requires 키워드로 시작하는 요구사항은 항상 중첩 요구사항(nested requirement)으로 해석됩니다. 따라서 단순 요구사항(simple requirement)은 괄호로 묶이지 않은 requires 표현식으로 시작할 수 없습니다.
타입 요구사항
typename
identifier
;
|
|||||||||
| identifier | - | (가능성 있는 한정된) identifier ( 단순 템플릿 식별자 포함) |
타입 요구사항은
identifier
로 명명된 타입이 유효함을 주장합니다: 이는 특정 이름의 중첩 타입이 존재하는지 확인하거나, 클래스/별칭 템플릿 특수화가 타입을 명명하는지 검증하는 데 사용될 수 있습니다. 클래스 템플릿 특수화를 명명하는 타입 요구사항은 타입이 완전할 것을 요구하지 않습니다.
template<typename T> using Ref = T&; template<typename T> concept C = requires { typename T::inner; // 요구되는 중첩 멤버 이름 typename S<T>; // 요구되는 클래스 템플릿 특수화 typename Ref<T>; // 요구되는 별칭 템플릿 치환 }; template<class T, class U> using CommonType = std::common_type_t<T, U>; template<class T, class U> concept Common = requires (T&& t, U&& u) { typename CommonType<T, U>; // CommonType<T, U>가 유효하고 타입을 명명함 { CommonType<T, U>{std::forward<T>(t)} }; { CommonType<T, U>{std::forward<U>(u)} }; };
복합 요구사항
{
expression
};
|
(1) | ||||||||
{
expression
}
noexcept
;
|
(2) | ||||||||
{
expression
} ->
type-constraint
;
|
(3) | ||||||||
{
expression
}
noexcept ->
type-constraint
;
|
(4) | ||||||||
| expression | - | 표현식 |
| type-constraint | - | constraint |
복합 요구사항은
expression
의 속성을 주장합니다. 치환과 의미론적 제약 조건 검사는 다음 순서로 진행됩니다:
expression 는 평가되지 않는 피연산자 입니다.
template<typename T> concept C2 = requires(T x) { // *x 표현식이 유효해야 함 // AND T::inner 타입이 유효해야 함 // AND *x의 결과가 T::inner로 변환 가능해야 함 {*x} -> std::convertible_to<typename T::inner>; // x + 1 표현식이 유효해야 함 // AND std::same_as<decltype((x + 1)), int>가 만족되어야 함 // 즉, (x + 1)은 int 타입의 prvalue여야 함 {x + 1} -> std::same_as<int>; // x * 1 표현식이 유효해야 함 // AND 그 결과가 T로 변환 가능해야 함 {x * 1} -> std::convertible_to<T>; };
중첩 요구사항
requires
제약-식
;
|
|||||||||
| constraint-expression | - | 제약 조건을 나타내는 표현식 |
중첩 요구 사항은 지역 매개변수 측면에서 추가적인 제약 조건을 지정하는 데 사용될 수 있습니다.
constraint-expression
는 (존재하는 경우) 치환된 템플릿 인자들에 의해 충족되어야 합니다. 중첩 요구 사항으로의 템플릿 인자 치환은
constraint-expression
이 충족되는지 여부를 결정하는 데 필요한 범위까지만
constraint-expression
내에서 치환을 수행합니다.
template<class T> concept Semiregular = DefaultConstructible<T> && CopyConstructible<T> && CopyAssignable<T> && Destructible<T> && requires(T a, std::size_t n) { requires Same<T*, decltype(&a)>; // 중첩: "Same<...>가 true로 평가됨" { a.~T() } noexcept; // 복합: "a.~T()"는 예외를 던지지 않는 유효한 표현식 requires Same<T*, decltype(new T)>; // 중첩: "Same<...>가 true로 평가됨" requires Same<T*, decltype(new T[n])>; // 중첩 { delete new T }; // 복합 { delete new T[n] }; // 복합 };
참고
키워드 requires 는 또한 requires clauses 를 도입하는 데에도 사용됩니다.
template<typename T> concept Addable = requires (T x) { x + x; }; // requires 표현식 template<typename T> requires Addable<T> // requires 절, requires 표현식이 아님 T add(T a, T b) { return a + b; } template<typename T> requires requires (T x) { x + x; } // ad-hoc 제약 조건, 키워드가 두 번 사용됨에 유의 T add(T a, T b) { return a + b; }
키워드
결함 보고서
다음의 동작 변경 결함 보고서들은 이전에 발표된 C++ 표준에 소급 적용되었습니다.
| DR | 적용 대상 | 게시된 동작 | 올바른 동작 |
|---|---|---|---|
| CWG 2560 | C++20 | 매개변수 타입이 requires 표현식에서 조정되는지 불명확했음 | 동일하게 조정됨 |
| CWG 2911 | C++20 |
requires
표현식 내부의 모든 표현식이 평가되지 않은 피연산자로 간주됨 |
일부
표현식만 해당됨 |
참고문헌
- C++23 표준 (ISO/IEC 14882:2024):
-
- 7.5.7 Requires 표현식 [expr.prim.req]
- C++20 표준(ISO/IEC 14882:2020):
-
- 7.5.7 요구 표현식 [expr.prim.req]
참고 항목
| 제약 조건과 개념 (C++20) | 템플릿 인수에 대한 요구 사항을 지정함 |