왜 요구가 필요한가?


161

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

또한 require-expressionrequire-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 ++에서 비용이 듭니다. 개념의 제안은 이미 두 개의 키워드를 소개 : conceptrequires. 문법적인 문제가없고 사용자가 직면 한 문제가 거의없는 두 번째 사례를 모두 처리 할 수있는 세 번째 사례를 소개하는 것은 낭비입니다. 결국, 유일한 시각적 문제는 키워드가 두 번 반복된다는 것입니다.
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 && Qrequires (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.