C ++ 20 'requires'표현식에서 지정되지 않은 유형을 어떻게 사용할 수 있습니까?

Aug 16 2020

저는 C ++ 20 개념을 작성하여 유형에 인수를받는 특정 메서드가 있어야한다는 요구 사항을 표현하려고하지만이 개념의 목적 상 인수 유형이 무엇인지는 신경 쓰지 않습니다.

다음과 같이 작성하려고했습니다.

template <typename T>
concept HasFooMethod = requires(T t, auto x)
{
    { t.Foo(x) } -> std::same_as<void>;
};

그러나 gcc와 clang은 모두이를 거부하여 require 표현식의 매개 변수 목록에서 이러한 방식으로 'auto'를 사용할 수 없다는 오류를 표시합니다.

대안은 'x'유형을 두 번째 템플릿 매개 변수로 넣는 것입니다.

template <typename T, typename TX>
concept HasFooMethod = requires(T t, TX x)
{
    { t.Foo(x) } -> std::same_as<void>;
};

그러나 이것은 개념이 사용될 때마다 TX를 명시 적으로 지정해야하며 추론 할 수 없습니다.

struct S { void Foo(int); };
static_assert(HasFooMethod<S>);         // doesn't compile
static_assert(HasFooMethod<S, int>);    // the 'int' must be specified

Foo가 지정되지 않은 유형 의 인수를 취할 수있는 개념을 작성하는 방법이 있습니까?

제한된 템플릿 멤버 함수를 요구하는 개념 정의 라는 질문 은 매우 유사하지만 동일하지는 않습니다.이 질문은 (템플릿) 메서드가 주어진 개념을 충족하는 모든 유형을 취할 수 있도록 요구하는 방법을 묻는 반면,이 질문은 메서드가 일부 특정 유형의 해당 유형은 지정되지 않습니다,하지만. 수량화 측면에서 다른 질문은 (제한된) 보편적 수량화에 대해 묻는 것이고, 이것은 실존 적 수량화에 관한 것입니다. 다른 질문의 답변도 제 경우에 적용되지 않습니다.

답변

4 NicolBolas Aug 17 2020 at 13:48

개념은 원하는 기능을 제공하기위한 것이 아닙니다. 그래서 그들은 그것을 제공하지 않습니다.

개념은 템플릿을 제한하고 템플릿이 정의에서 사용하려는 (또는 최소한 자유롭게 사용할 수있는) 식 또는 문 집합을 지정하기위한 것입니다.

템플릿 내에서 당신은 그래서 당신은 식을 작성하는 경우, 구속되어 t.Foo(x), 다음의 유형을 알고있다 x. 구체적인 유형, 템플릿 매개 변수 또는 템플릿 매개 변수에서 파생 된 이름입니다. 어느 쪽이든 x제한되는 템플릿에서 유형을 사용할 수 있습니다.

따라서 이러한 템플릿을 제한하려면의 유형 t과 유형을 모두 사용합니다 x. 그 때 둘 다 사용할 수 있으므로 이러한 제약을 만드는 데 문제가 없습니다. 즉, 제약 조건이 T격리 된 유형으로 설정 되어 있지 않습니다 . 그것은 사이의 연관에의 TX.

개념은 제약 조건의 실제 사용 위치와 관련이없는 진공 상태에서 작동하기위한 것이 아닙니다. 사용자가 static_assert자신의 클래스에 맞설 수 있도록 단항 개념을 만드는 데 집중해서는 안됩니다 . 개념은 유형이이를 충족하는지 테스트하기위한 것이 아닙니다 (기본적으로 static_assert수행하는 작업입니다). 그것들을 사용 하는 템플릿 정의를 제한하기위한 것입니다.

귀하의 제약 요구가되게 FooCallableWith하지 HasFooMethod.

NathanReed Aug 17 2020 at 16:30

이와 유사한 것은 암시 적으로 (거의) 무엇이든 변환 할 수있는 어댑터 유형을 정의하여 수행 할 수 있습니다.

struct anything
{
    // having both these conversions allows Foo's argument to be either
    // a value, an lvalue reference, or an rvalue reference

    template <typename T>
    operator T&();

    template <typename T>
    operator T&&();
};

이러한 연산자는 평가되지 않은 컨텍스트에서만 사용되므로 구현할 필요가 없습니다 (실제로 모든 유형 T에 대해 구현할 수 없음).

그런 다음 다음과 HasFooMethod같이 작성할 수 있습니다.

template <typename T>
concept HasFooMethod = requires(T t, anything a)
{
    { t.Foo(a) } -> std::same_as<void>;
};