C ++ 20 개념 : 템플릿 인수가 여러 개념에 적합한 경우 어떤 템플릿 전문화가 선택됩니까?


23

주어진 :

#include <concepts>
#include <iostream>

template<class T>
struct wrapper;

template<std::signed_integral T>
struct wrapper<T>
{
    wrapper() = default;
    void print()
    {
        std::cout << "signed_integral" << std::endl;
    }
};

template<std::integral T>
struct wrapper<T>
{
    wrapper() = default;
    void print()
    {
        std::cout << "integral" << std::endl;
    }
};

int main()
{
    wrapper<int> w;
    w.print(); // Output : signed_integral
    return 0;
}

위의 코드에서 개념 과 개념 int을 모두 충족 합니다.std::integralstd::signed_integral

놀랍게도 이것은 GCC 및 MSVC 컴파일러 모두에서 "signed_integral"을 컴파일하고 인쇄합니다. "템플릿 전문화가 이미 정의되어 있습니다."라는 내용의 오류로 인해 실패 할 것으로 예상했습니다.

그래, 그것은 합법적이고 충분히 공평하지만 왜 std::signed_integral대신에 선택 되었는가 std::integral? 여러 개념이 템플릿 인수에 해당 될 때 어떤 템플릿 전문화가 선택되는지 표준에 정의 된 규칙이 있습니까?


나는 컴파일러가 그것을 받아들이는 초기 단계에서 그것을 받아 들인다는 사실만으로 합법적이라고 말하지 않을 것이다.
Slava

그들이 직관적 인 방식으로 서로를 포괄하므로, 개념주의 깊게 설계이 경우에 @Slava
기욤 Racicot의

@GuillaumeRacicot는 괜찮습니다. "컴파일러가 받아들이 기 때문에 합법적입니다"라는 결론은 오해의 소지가 있습니다. 나는 이것이 합법적이지 않다고 말하지 않았다.
Slava

답변:


14

이는 템플릿 순서 자체와 같은 개념이 다른 개념보다 전문화 될 수 있기 때문입니다. 이것을 제약의 부분 순서 라고 합니다

개념의 경우, 이들은 동등한 제한 조건을 포함 할 때 서로를 포함합니다. 예를 들어, 방법 std::integralstd::signed_integral구현 방법은 다음 과 같습니다.

template<typename T>
concept integral = std::is_integral_v<T>;

template<typename T> //   v--------------v---- Using the contraint defined above
concept signed_integral = std::integral<T> && std::is_signed_v<T>;

제약 조건을 정규화하면 컴파일러는 제약 조건 식을 다음과 같이 정리합니다.

template<typename T>
concept integral = std::is_integral_v<T>;

template<typename T>
concept signed_integral = std::is_integral_v<T> && std::is_signed_v<T>;

이 예에서는 완전히 signed_integral암시 integral합니다. 어떤 의미에서 부호있는 적분은 적분보다 "제한적"입니다.

표준은 다음과 같이 작성합니다.

가입일 [temp.func.order / 2 (강조 내) :

부분 순서는 각 템플릿을 차례로 변환하고 (다음 단락 참조) 함수 유형을 사용하여 템플릿 인수 추론을 수행하여 두 함수 템플릿 중 다른 것보다 더 전문화 된 기능 템플릿을 선택합니다. 추론 프로세스는 템플릿 중 하나가 다른 템플릿보다 더 전문적인지 여부를 결정합니다. 그렇다면 부분적 주문 프로세스에서 선택한 템플릿이보다 전문화 된 템플릿입니다. 두 추론이 모두 성공하면 부분 순서는 [temp.constr.order] 의 규칙에 설명 된대로보다 제한된 템플릿을 선택합니다 .

즉, 템플릿을 여러 번 대체 할 수 있고 둘 다 부분 순서에서 선택하면 가장 제한된 템플릿을 선택합니다.

[temp.constr.order] / 1 부터 :

제약 조건 P가 포섭 제약 Q 마다 논리합 절을위한 경우에만,, P I 의 논리합 정규형 P가 , P 각 접속어 절 포섭 Q의 J 의 논리 곱 표준형의 Q를 ,

  • 논리합 절 P는 인 결합 절 포섭 Q의 J를 원자 제약이 존재하는 경우에만, P의 IAP 내가 되는 원자 제약이 존재 Q의 JBQ의 J가 되도록 P IA 포섭 Q의 JB를 하고

  • 원자 제한 조건 A[temp.constr.atomic]에 설명 된 규칙을 사용하여 AB 가 동일한 경우에만 다른 원자 제한 조건 B를 가정 합니다.

이것은 컴파일러가 제약 조건을 순서화하는 데 사용하는 하위 가정 알고리즘과 개념을 설명합니다.


2
당신은 단락의 중간에 후행처럼 보인다 ...
ShadowRanger

11

C ++ 20에는 특정 제한 엔티티가 다른 제한 엔티티보다 "더 제한적"인시기를 결정하는 메커니즘이 있습니다. 이것은 간단한 것이 아닙니다.

이것은 제약 조건을 원자 구성 요소 ( 제약 정규화 라고하는 프로세스)로 분해하는 개념으로 시작합니다 . 여기에 들어가는 것은 너무 복잡하고 복잡하지만 기본 개념은 구속 조건의 각 표현식이 개념이 아닌 구성 요소 하위 표현식에 도달 할 때까지 재귀 적으로 원자 적 개념으로 분류된다는 것입니다.

그러므로 integral, signed_integral개념 과 개념 어떻게 정의 되는지 봅시다 :

template<class T>
  concept integral = is_integral_v<T>;
template<class T>
  concept signed_­integral = integral<T> && is_signed_v<T>;

의 분해 integral는 단지 is_integral_v입니다. 의 분해는 signed_integral입니다 is_integral_v && is_signed_v.

이제 제약 조건 추정 의 개념을 살펴 보자 . 복잡하지만, 기본 아이디어는 C1의 분해에 C2의 모든 하위 표현식이 포함 된 경우 제약 조건 C1이 제약 조건 C2를 "취득"한다고합니다. 우리는이 볼 수있는 integral포괄하지 않습니다 signed_integral,하지만 signed_integral 하지 썸을 integral이 모든 것을 포함하고 있기 때문에, integral않습니다.

다음으로 제한된 엔티티를 주문합니다.

* D1과 D2가 모두 구속 된 선언이고 D1의 관련 제약 조건이 D2의 제약 조건을 가정하면 선언 D1은 선언 D2만큼 제한된다. 또는 * D2에는 연관된 구속 조건이 없습니다.

signed_integral포함 하기 때문에 integral, <signed_integral> wrapper<integral> wrapper. 그러나 하위 가정이 되돌릴 수 없기 때문에 그 반대가 아닙니다.

따라서 "보다 제한적인"엔터티에 대한 규칙에 따라 :

D1이 적어도 D2만큼 구속되고 D2가 D1만큼 구속되지 않은 경우 선언 D1은 다른 선언 D2보다 더 구속됩니다.

(가) 이후 <integral> wrapper와 같은 제약으로 적어도 아닌 <signed_integral> wrapper, 후자는 이전보다 더 제한된 생각된다.

따라서 두 사람 모두가 적용될 수 있으면 더 구속 된 선언이 이깁니다.


제약 조건 Subsumption의 규칙은 표현식이 아닌 표현식이 발견되면 중지 됩니다 concept. 따라서이 작업을 수행 한 경우 :

template<typename T>
constexpr bool my_is_integral_v = std::is_integral_v<T>;

template<typename T>
concept my_signed_integral = my_is_integral_v<T> && std::is_signed_v<T>;

이 경우에는 my_signed_integral 포함 하지 않습니다std::integral . 와 my_is_integral_v동일하게 정의되어 있지만 std::is_integral_v개념이 아니기 때문에 C ++의 Subsumption 규칙은이를 통해 피어 규칙이 동일한 지 판단 할 수 없습니다.

따라서 추정 규칙을 사용하면 원자 개념에 대한 개념을 벗어난 개념을 작성할 수 있습니다.


3

Partial_ordering_of_constraints

P가 Q가 P와 Q에서 원자 적 제약의 동일성을 의미한다는 것을 증명할 수 있다면 제약 조건 P는 제약 조건 Q를 가정한다고한다.

Subsumption relation은 다음을 결정하는 데 사용되는 제약 조건의 부분 순서를 정의합니다.

  • 과부하 해결에서 비 템플릿 기능에 가장 적합한 후보
  • 과부하 세트에있는 템플릿이 아닌 함수의 주소
  • 템플릿 템플릿 인수와 가장 일치
  • 클래스 템플릿 전문화의 부분 순서
  • 함수 템플릿의 부분 순서

그리고 개념 std::signed_integralstd::integral<T>개념을 가정합니다.

template < class T >
concept signed_integral = std::integral<T> && std::is_signed_v<T>;

std::signed_integral더 전문화 된 것처럼 코드도 괜찮습니다 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.