same_as 개념이 유형 평등을 두 번 검사하는 이유는 무엇입니까?


19

https://en.cppreference.com/w/cpp/concepts/same_as 에서 same_as 개념의 가능한 구현을 살펴보면 이상한 일이 일어나고 있음을 알았습니다.

namespace detail {
    template< class T, class U >
    concept SameHelper = std::is_same_v<T, U>;
}

template< class T, class U >
concept same_as = detail::SameHelper<T, U> && detail::SameHelper<U, T>;

첫 번째 질문은 SameHelper개념이 왜 엉망인가? 두 번째 이유는 same_as만약 검사 T와 동일 U하고 U같은 T? 중복되지 않습니까?


그냥 때문에 SameHelper<T, U>의미하지 않는다 사실 일 수도 SameHelper<U, T>수 있습니다.
일부 프로그래머 친구

1
그것은 a가 b와 같다면 b는 a와 같지 않습니까?
user7769147

@ user7769147 예, 이것이 그 관계를 정의하고 있습니다.
François Andrieux

4
흠에 대한 문서 표준 : is_same는 심지어 "교환 법칙이 성립는 두 종류의 T와 U, 대한, 즉 만족 말합니다 is_same<T, U>::value == true경우에만, is_same<U, T>::value == true." 이것은이 이중 점검이 필요하지 않음을 의미합니다.
Kevin

1
아니오, 이것은 틀 렸습니다. std :: is_same는 말합니다 : 조건이 유지되는 경우에만 두 가지 유형이 교환 가능합니다. 반드시 그런 것은 아닙니다. 그러나 나는 두 가지 비 정류 유형의 예를 찾지 못했습니다.
Nemanja Boric

답변:


16

흥미로운 질문입니다. 나는 최근 Andrew Sutton의 Concepts에 대한 이야기를 보았고 Q & A 세션에서 누군가 다음 질문 (다음 링크의 타임 스탬프)을 물었습니다 : CppCon 2018 : Andrew Sutton

문제가 귀결 그래서 : If I have a concept that says A && B && C, another says C && B && A, would those be equivalent?앤드류 '예'라고 대답하지만, 컴파일러는 일부 내부 (사용자에게 투명) 방법 (원자 논리적 명제로 개념을 분해 할 수있는 사실을 지적 atomic constraints그들이 여부 앤드류 용어 말로로) 체크를 동등한.

이제 cppreference가 무엇을 말하는지 살펴보십시오 std::same_as.

std::same_as<T, U>서브 섬 std::same_as<U, T>과 그 반대.

기본적으로 "만약의 경우"관계입니다. 서로를 의미합니다. (논리적 동등성)

필자의 결론은 원자 적 제약이 있다는 std::is_same_v<T, U>것이다. 방법 컴파일러 치료는 std::is_same_v그들이 생각하게 할 수 std::is_same_v<T, U>std::is_same_v<U, T>두 개의 서로 다른 제약 조건으로 (서로 다른 엔티티입니다!). 따라서 std::same_as그중 하나만 사용하여 구현 하는 경우 :

template< class T, class U >
concept same_as = detail::SameHelper<T, U>;

그런 다음 std::same_as<T, U>std::same_as<U, T>다른 원자 제약을에 "폭발"과 동일하지 될 것입니다.

글쎄, 왜 컴파일러는 신경 쓰는가?

이 예제를 고려하십시오 .

#include <type_traits>
#include <iostream>
#include <concepts>

template< class T, class U >
concept SameHelper = std::is_same_v<T, U>;

template< class T, class U >
concept my_same_as = SameHelper<T, U>;

// template< class T, class U >
// concept my_same_as = SameHelper<T, U> && SameHelper<U, T>;

template< class T, class U> requires my_same_as<U, T>
void foo(T a, U b) {
    std::cout << "Not integral" << std::endl;
}

template< class T, class U> requires (my_same_as<T, U> && std::integral<T>)
void foo(T a, U b) {
    std::cout << "Integral" << std::endl;
}

int main() {
    foo(1, 2);
    return 0;
}

이상적으로는 다음을 my_same_as<T, U> && std::integral<T>포함합니다 my_same_as<U, T>. 따라서 컴파일러는 두 번째 템플릿 전문화를 선택해야합니다. 단, 다음과 같은 경우는 예외 error: call of overloaded 'foo(int, int)' is ambiguous입니다. 컴파일러에서 오류가 발생 합니다.

이 뒤에 그 이유는 이후 있다는 것입니다 my_same_as<U, T>my_same_as<T, U>서로를 포섭하지 않습니다 my_same_as<T, U> && std::integral<T>my_same_as<U, T>(포섭의 관계에서 제약 부분적으로 정렬 된 세트) 비교할된다.

그러나 교체하면

template< class T, class U >
concept my_same_as = SameHelper<T, U>;

template< class T, class U >
concept my_same_as = SameHelper<T, U> && SameHelper<U, T>;

코드가 컴파일됩니다.


same_as <T, U> 및 same_as <U, T>도 다른 원자 제약 조건이 될 수 있지만 결과는 여전히 동일합니다. 컴파일러가 논리적 관점에서 동일한 두 개의 다른 원자 제약 조건으로 same_as를 정의하는 데 많은 관심을 갖는 이유는 무엇입니까?
user7769147

2
컴파일러입니다 필요 고려하는 어떤 제약 포섭을위한 별개로이 개 표현을하지만, 고려할 수있는 인자 명백한 방식으로 그들에게 있습니다. 따라서 우리는 양방향을 필요로 할뿐만 아니라 (제약 조건을 비교할 때 이름이 어떤 순서로 명명되는지는 중요하지 않습니다), 또한 SameHelper두 가지 용도is_same_v동일한 표현에서 파생되도록합니다.
Davis Herring

@ user7769147 업데이트 된 답변보기.
린 Kaenbyou

1
개념 평등과 관련하여 기존의 지혜가 잘못된 것 같습니다. 와 is_same<T, U>동일한 템플릿과 달리 is_same<U, T>두 개의 원자 제한 조건은 동일한 표현식으로 구성되지 않는 한 동일한 것으로 간주되지 않습니다. 따라서 둘 다 필요합니다.
AndyG

무엇에 대해 are_same_as? template<typename T, typename U0, typename... Un> concept are_same_as = SameAs<T, U0> && (SameAs<T, Un> && ...);어떤 경우에는 실패합니다. 예를 들어 are_same_as<T, U, int>에 해당 될 are_same_as<T, int, U>수 있지만에are_same_as<U, T, int>
user7769147

2

std::is_same 다음과 같은 경우에만 true로 정의됩니다.

T와 U의 이름은 동일한 cv-qualifications로 동일한 유형입니다.

내가 아는 한, 표준은 "동일한 유형"의 의미를 정의하지 않지만, 자연 언어와 논리에서 "동일한"은 동등한 관계이며 따라서 교환 적입니다.

내가 주장한 이러한 가정을 감안할 때 is_same_v<T, U> && is_same_v<U, V>실제로 중복 될 것입니다. 그러나 same_­as측면에서 지정되지 않은 is_same_v; 그것은 단지 박람회를위한 것입니다.

두 가지 모두에 대한 명시 적 검사를 통해 구현 이 정식화되지 않고 same-as-impl만족할 same_­as수 있습니다. 이 방법으로 지정하면 구현 방법을 제한하지 않고 개념의 작동 방식을 정확하게 설명합니다.

정확히이 방법을로 지정하지 않고 선택한 이유는 is_same_v모르겠습니다. 선택된 접근법의 장점은 두 정의가 분리되어 있다는 것입니다. 하나는 다른 것에 의존하지 않습니다.


2
나는 당신에 동의하지만,이 마지막 주장은 약간의 확장입니다. "저는 두 가지 유형이 같은지 알려주는 재사용 가능한 구성 요소를 가지고 있습니다. 이제는 이전 구성 요소를 재사용하는 대신 유형이 같은지 알아야하는 다른 구성 요소가 있습니다. "이 사건에 특화된 임시 해결책을 만들겠습니다. 이제 평등의 정의를 가진 사람과 평등의 정의를 필요로하는 사람을 분리했습니다."
Cássio Renan

1
@CassioRenan 물론입니다. 내가 말했듯이, 나는 왜 그런지 모르겠다. 그것이 내가 생각해 낼 수있는 최고의 추론이다. 저자는 더 나은 근거를 가질 수 있습니다.
eerorika
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.