왜 요구가 필요한가?


161

C ++ 20 개념의 모퉁이 중 하나는 작성해야하는 특정 상황이 있다는 것 requires requires입니다. 예를 들어, [expr.prim.req] / 3 의이 예는 다음과 같습니다 .

또한 require-expression 은 require-clause ([temp])에서 템플릿 인수에 대한 임시 제한 조건을 작성하는 방법으로 다음과 같이 사용될 수 있습니다.

template<typename T>
  requires requires (T x) { x + x; }
    T add(T a, T b) { return a + b; }

첫 번째는 require -clause를 소개하고 두 번째는 require-expression을 소개합니다 .

두 번째 requires키워드 가 필요한 기술적 이유는 무엇입니까 ? 왜 우리는 글쓰기를 허용 할 수 없습니까?

template<typename T>
  requires (T x) { x + x; }
    T add(T a, T b) { return a + b; }

(참고 : 문법에 답하지 마십시오 requires)


25
제안 : "필요한 것이 있습니까?" 더 진지하게, 나는 그것이 같은 이유 뒤에 직감이 있습니다 noexcept(noexcept(...)).
— Quentin

11
이 두 requires가지는 내 의견으로는 동음 이의어이다 : 그것들은 똑같아 보이고, 철자가 같고, 냄새는 같지만 본질적으로 다르다. 수정을 제안하려면 그 중 하나의 이름을 바꾸는 것이 좋습니다.
— YSC

5
@YSC- co_requires? (죄송합니다. 저항 할 수 없었습니다).
— StoryTeller-Unslander Monica

122
광기는 어디에서 멈출 것인가? 다음으로 알 수있는 것은 다음과 같습니다 long long.
— Eljay

8
@StoryTeller : "필요합니까?" 훨씬 더 문맹 적이었을 것입니다!
— PW

답변:


81

문법에 필요하기 때문입니다. 그렇습니다.

requires제약하지 않는 해야 할 용도 requires표현. 임의의 부울 상수 표현식을 사용할 수 있습니다. 따라서 requires (foo)합법적 인 requires제약 조건 이어야합니다 .

requires 표현 독특한 구조이다 (어떤 일 특정 제약 조건에 따라 여부를 테스트하는 것이 그 것); 동일한 키워드로 소개되었습니다. requires (foo f)유효한 requires표현 의 시작입니다 .

당신이 원하는 것은 requires제약 조건을 받아들이는 장소에서 사용한다면 , requires절 에서 "제한 + 표현"을 만들 수 있어야한다는 것 입니다.

그래서 여기에 질문이 있습니다 : 당신이 requires (foo)요구 제약 조건에 적합한 장소에 넣으면 파서가 얼마나 멀리 갈 필요가 있는지 당신이 원하는 방식으로 제약 조건 + 표현보다는 요구 제약 조건 임을 깨닫기 전에 되려고?

이걸 고려하세요:

void bar() requires (foo)
{
  //stuff
}

foo유형 인 경우 (foo)필수 표현식의 매개 변수 목록이며의 모든 내용 {}은 함수의 본문이 아니라 해당 requires표현식 의 본문입니다 . 그렇지 않으면 절의 foo표현식입니다 requires.

글쎄, 당신은 컴파일러 foo가 먼저 무엇을 알아 내야한다고 말할 수 있습니다. 그러나 C ++ 은 토큰 시퀀스를 구문 분석하는 기본 동작에서 컴파일러가 토큰을 이해하기 전에 해당 식별자의 의미를 파악해야 할 때 실제로 그것을 좋아하지 않습니다. 예, C ++는 상황에 따라 달라 지므로 이런 일이 발생합니다. 그러나위원회는 가능한 한 피하는 것을 선호한다.

그래, 문법이야.


2
유형이 있지만 이름이없는 매개 변수 목록을 갖는 것이 합리적입니까?
— NathanOliver 2016 년

3
@Quentin : C ++ 문법에는 상황에 따라 달라지는 경우가 있습니다. 그러나위원회는 실제로이를 최소화하려고 노력하고 있으며 더 추가하는 것을 좋아하지 않습니다 .
— Nicol Bolas

3
@RobertAndrzejuk : 템플릿 인수 집합 requires이후 <>또는 함수 매개 변수 목록 뒤에 나타나는 경우 require -clause입니다. 경우 requires표현식이 유효 나타납니다, 다음은이 요구하는 표현이다. 이는 구문 분석 트리의 내용 이 아니라 구문 분석 트리의 구조에 의해 결정될 수 있습니다 (식별자가 어떻게 정의되는지는 트리의 내용 일 것임).
— Nicol Bolas

6
@RobertAndrzejuk : 물론, require-expression이 다른 키워드를 사용했을 수도 있습니다. 그러나 키워드는 키워드가 된 식별자를 사용하는 모든 프로그램을 중단 할 가능성이 있으므로 C ++에서 큰 비용이 듭니다. 개념의 제안은 이미 두 개의 키워드를 소개 : concept와 requires. 문법적인 문제가없고 사용자가 직면 한 문제가 거의없는 두 번째 사례를 모두 처리 할 수있는 세 번째 사례를 소개하는 것은 낭비입니다. 결국, 유일한 시각적 문제는 키워드가 두 번 반복된다는 것입니다.
— Nicol Bolas

3
@RobertAndrzejuk 어쨌든 개념을 작성한 것처럼 하위 가정을 얻지 못하므로 제약 조건을 인라인하는 것은 좋지 않습니다. 따라서 사용하지 않는 권장 기능이 적은 식별자를 사용하는 것은 좋은 생각이 아닙니다.
— Rakete1111

60

상황은와 정확히 유사합니다 noexcept(noexcept(...)). 물론 이것은 좋은 것보다 나쁜 것 같지만 설명하겠습니다. :) 우리는 당신이 이미 알고있는 것으로 시작할 것입니다 :

C ++ 11에는 " noexcept-clauses"및 "-expressions"가 noexcept있습니다. 그들은 다른 일을합니다.

  • noexcept-clause는 "이 기능은 말했다 때 ... noexcept해야한다 (일부 조건)." 함수 선언을 진행하고 부울 매개 변수를 사용하여 선언 된 함수에서 동작 변경을 발생시킵니다.

  • noexcept-expression는 "컴파일러는 말한다 여부를 말해주십시오 (일부 표현) noexcept입니다." 그 자체가 부울 표현식입니다. 프로그램 동작에 "부작용"이 없습니다. 예 / 아니오 질문에 대한 답변을 컴파일러에 요청하는 것입니다. "이 표현은 예외가 아닌가?"

우리는 할 수 둥지 noexcept, 안쪽 -expression noexcept-clause을하지만, 우리는 일반적으로 그것을 그렇게 나쁜 스타일을 고려한다.

template<class T>
void incr(T t) noexcept(noexcept(++t));  // NOT SO HOT

noexcept-expression을 type-trait 로 캡슐화하는 것이 더 나은 스타일로 간주됩니다 .

template<class T> inline constexpr bool is_nothrow_incrable_v =
    noexcept(++std::declval<T&>());  // BETTER, PART 1

template<class T>
void incr(T t) noexcept(is_nothrow_incrable_v<T>);  // BETTER, PART 2

C ++ 2a 작업 초안에는 " requires-clauses"및 "-expressions"가 requires있습니다. 그들은 다른 일을합니다.

  • - requires절은 "이 함수 는 ... (일부 조건) 일 때 과부하 해결에 참여해야합니다. "라고 말합니다 . 함수 선언을 진행하고 부울 매개 변수를 사용하여 선언 된 함수에서 동작 변경을 발생시킵니다.

  • - requires컴파일러는 "컴파일러, (일부 표현식 세트)가 제대로 구성되어 있는지 알려주십시오 ." 그 자체가 부울 표현식입니다. 프로그램 동작에 "부작용"이 없습니다. 예 / 아니오 질문에 대한 답변을 컴파일러에 요청하는 것입니다. "이 표현이 잘 구성되어 있습니까?"

우리는 할 수 둥지 requires, 안쪽 -expression requires-clause을하지만, 우리는 일반적으로 그것을 그렇게 나쁜 스타일을 고려한다.

template<class T>
void incr(T t) requires (requires(T t) { ++t; });  // NOT SO HOT

requires타입 표현에 표현 을 캡슐화하는 것이 더 나은 스타일로 간주됩니다 ...

template<class T> inline constexpr bool is_incrable_v =
    requires(T t) { ++t; };  // BETTER, PART 1

template<class T>
void incr(T t) requires is_incrable_v<T>;  // BETTER, PART 2

... 또는 (C ++ 2a Working Draft) 개념.

template<class T> concept Incrable =
    requires(T t) { ++t; };  // BETTER, PART 1

template<class T>
void incr(T t) requires Incrable<T>;  // BETTER, PART 2

1
나는이 주장을 실제로 사지 않는다. noexcept문제가 noexcept(f())의미 할 수 중 하나를 해석하는 f()우리가 규격을 설정하는 데 사용하고 있는지 부울 또는 여부 확인 f()입니다 noexcept. requires유효성을 검사하는 표현식은 이미 {}s 로 도입 되었기 때문에이 모호함이 없습니다 . 그 후, 논쟁은 기본적으로 "문법은 그렇게 말한다"입니다.
— Barry

@Barry : 이 의견을보십시오 . {}선택 사항으로 나타납니다 .
— Eric

1
@Eric {}선택 사항이 아니며 해당 의견이 표시하지 않습니다. 그러나 그것은 파싱 모호성을 보여주는 훌륭한 의견입니다. 아마도 그 의견을 (일부 설명과 함께) 독립형 답변으로 받아 들일 것입니다
— Barry

1
requires is_nothrow_incrable_v<T>;해야requires is_incrable_v<T>;
— 루슬란

16

나는 cppreference의 개념 페이지 가 이것을 설명 한다고 생각 합니다. "math"로 설명 할 수 있습니다. 왜 이것이 다음과 같아야하는지 :

개념을 정의하려면 다음을 수행하십시오.

template<typename T>
concept Addable = requires (T x) { x + x; }; // requires-expression

해당 개념을 사용하는 함수를 선언하려면 다음을 수행하십시오.

template<typename T> requires Addable<T> // requires-clause, not requires-expression
T add(T a, T b) { return a + b; }

이제 개념을 별도로 정의하고 싶지 않다면 대체해야 할 것 같습니다. 이 부품을 가져 와서 requires (T x) { x + x; };교체하면 다음과 같은 이점이 Addable<T>있습니다.

template<typename T> requires requires (T x) { x + x; }
T add(T a, T b) { return a + b; }

그것이 당신이 요구하는 것입니다.


4
나는 질문이 요구하는 것이라고 생각하지 않습니다. 이것은 문법을 어느 정도 설명하고 있습니다.
— Passer 작성자 :

그러나 왜 당신은 template<typename T> requires (T x) { x + x; }절과 표현 모두를 요구할 수 있고 요구할 수 없습니까?
— NathanOliver

2
@NathanOliver : 컴파일러가 한 구문을 다른 구문으로 해석하도록 강요하기 때문입니다. requires-as-제약 절을하지 않는 해야 수 requires-expression. 그것은 단지 하나의 가능한 사용입니다.
— Nicol Bolas

2
@TheQuantumPhysicist 내 의견으로 얻은 것은이 답변이 구문을 설명한다는 것입니다. 실제 기술적 인 이유가 아닙니다 requires requires. 그들은 문법에 무언가를 추가 할 수 template<typename T> requires (T x) { x + x; }있었지만 허용 하지 않았습니다. 배리는 그들이 왜하지 않았는지 알고 싶어
— NathanOliver

3
우리가 문법적으로 애매 모호함을 연주하고 있다면, 물겠습니다. godbolt.org/z/i6n8kM template<class T> void f(T) requires requires(T (x)) { (void)x; }; 은 requireses 중 하나를 제거하면 다른 의미를 갖습니다 .
— Quuxplusone

12

내가 발견 코멘트 난 그냥 그것의 거의 전체를 여기 인용라고 생각하므로,이 점에서 매우 도움이 될 (GCC에서 그것을 구현 된 개념의 저자 중 하나) 앤드류 서튼에서를 :

얼마 전 require-expressions (두 번째 요구에 의해 도입 된 문구)는 constraint-expressions (첫 번째 요구에 의해 도입 된 문구)에서 허용되지 않았습니다. 개념 정의에만 나타날 수 있습니다. 실제로, 이것은 그 주장이 나타나는 해당 논문의 섹션에서 제안 된 것과 정확히 같습니다.

그러나 2016 년에는 이러한 제한을 완화하기위한 제안이있었습니다 [편집자 주 : P0266 ]. 이 논문의 섹션 4에서 단락 4의 취소 선에 주목하십시오. 따라서 태어날 필요가 요구됩니다.

진실을 말하기 위해, 나는 실제로 GCC에서 그 제한을 구현하지 않았으므로 항상 가능했습니다. 월터가 그 사실을 발견하고 유용하다고 생각해서 그 논문을 이끌어 냈다고 생각합니다.

글쓰기에 민감하지 않다고 생각하는 사람이 두 배가 필요하지 않다면, 나는 그것이 단순화 될 수 있는지 결정하려고 시간을 보냈습니다. 짧은 대답 : 아닙니다.

문제는 템플릿 매개 변수 목록 다음에 도입해야하는 두 가지 문법 구조가 있다는 P && Q것 requires (T a) { ... }입니다. 이것을 요구 표현이라고합니다.

첫 번째는 제약 조건을 도입해야합니다. 두 번째는 require-expression을 요구합니다. 그것은 문법이 구성되는 방식입니다. 전혀 혼란스럽지 않습니다.

나는 한 번에 이것을 단일 요구로 축소하려고 시도했다. 불행히도, 이것은 심각한 파싱 문제를 야기합니다. 예를 들어 (after 요구가 중첩 된 하위 표현식 또는 매개 변수 목록을 나타내는 경우 쉽게 알 수 없습니다 . 나는 그 구문에 대한 명확한 명확성이 있다고 믿지 않습니다 (통일 초기화 구문에 대한 이론적 근거를보십시오.이 문제도 있습니다).

따라서 make는 표현식을 도입하거나 (현재와 같이) 매개 변수화 된 요구 사항 목록을 도입해야합니다.

대부분의 시간 (거의 100 %에서와 같이)에서 요구 표현 이외의 것을 원하기 때문에 현재 접근 방식을 선택했습니다. 그리고 매우 드문 경우에 나는 특별 제약 조건에 대해 require-expression을 원했습니다. 단어를 두 번 쓰는 것은 정말로 마음에 들지 않습니다. 템플릿에 대해 충분히 건전한 추상화를 개발하지 않았 음을 나타내는 명백한 지표입니다. (내가 가지고 있다면 이름이있을 것입니다.)

요구 사항에 require-expression을 도입하도록 선택할 수있었습니다. 실제로 모든 제약 조건이 다음과 같이 보이기 때문에 실제로 더 나쁩니다.

template<typename T>
  requires { requires Eq<T>; }
void f(T a, T b);

여기서 두 번째 요구 사항을 중첩 요구 사항이라고합니다. 표현식을 평가합니다 (Requires-expression 블록의 다른 코드는 평가되지 않습니다). 나는 이것이 현 상태보다 훨씬 나쁘다고 생각합니다. 이제 글쓰기가 두 번 필요합니다.

더 많은 키워드를 사용할 수도있었습니다. 이것은 그 자체의 문제입니다. 자전거 흘림 만이 아닙니다. 중복을 피하기 위해 키워드를 "재분배"할 수있는 방법이있을 수 있지만, 그렇게 진지한 생각은하지 않았습니다. 그러나 이것이 문제의 본질을 바꾸지는 않습니다.


-10

A에 요구 사항 B가 있고 요구 사항 B에 요구 사항 C가 있다고 말하고 있기 때문입니다.

A에는 B가 필요하고 C에는 C가 필요합니다.

"requires"절 자체에는 무언가가 필요합니다.

물건 A (B 필요 (C 필요))가 있습니다.

Meh. :)


4
그러나 다른 답변에 따르면 첫 번째와 두 번째 requires개념은 개념적으로 동일하지 않습니다 (하나는 절, 하나는 표현). 사실, 내가 올바르게 이해하면 ()in 의 두 세트 requires (requires (T x) { x + x; })는 매우 다른 의미를 갖습니다 (외부는 선택적이며 항상 부울 constexpr를 포함합니다. 내부는 필수 표현을 도입하고 실제 표현을 허용 하지 않는 필수 부분입니다 ).
— 최대 Langhof

2
@MaxLanghof 요구 사항이 다르다고 말하는가? : D
— 궤도의 가벼움 경주
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.