Tại sao khái niệm same_as kiểm tra loại bằng hai lần?


19

Nhìn vào khả năng triển khai của khái niệm same_as tại https://en.cppreference.com/w/cpp/con accept / same_as tôi nhận thấy điều gì đó kỳ lạ đang xảy ra.

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>;

Câu hỏi đầu tiên là tại sao một SameHelperkhái niệm được nedded? Thứ hai là tại sao same_askiểm tra nếu Tgiống UUgiống như T? Nó không dư thừa à?


Chỉ vì SameHelper<T, U>có thể đúng không có nghĩa là SameHelper<U, T>có thể.
Một số lập trình viên anh chàng

1
đó là điểm, nếu a bằng b, b bằng a phải không?
dùng7769147

@ user7769147 Có, và điều này đang xác định mối quan hệ đó.
François Andrieux

4
Hmm tài liệu cho std :: is_same thậm chí còn nói "Giao hoán được thỏa mãn, tức là đối với bất kỳ hai loại T và U, is_same<T, U>::value == truenếu và chỉ khi is_same<U, T>::value == true." Điều này ngụ ý rằng kiểm tra kép này là không cần thiết
Kevin

1
Không, điều này là sai, std :: is_same nói: nếu và chỉ khi điều kiện giữ, hai loại là giao hoán. Điều này không nhất thiết phải như vậy. Nhưng tôi không tìm thấy ví dụ về hai loại không giao hoán.
Nemanja Boric

Câu trả lời:


16

Câu hỏi thú vị. Gần đây tôi đã xem cuộc nói chuyện của Andrew Sutton về các khái niệm và trong phiên hỏi đáp có ai đó đã hỏi câu hỏi sau (dấu thời gian trong liên kết sau): CppCon 2018: Andrew Sutton miếng Khái niệm trong 60: Mọi thứ bạn cần biết và không có gì bạn không biết

Vì vậy, câu hỏi rút ra: If I have a concept that says A && B && C, another says C && B && A, would those be equivalent?Andrew trả lời là có, nhưng chỉ ra thực tế trình biên dịch có một số phương thức bên trong (rõ ràng đối với người dùng) để phân tách các khái niệm thành các mệnh đề logic nguyên tử ( atomic constraintsnhư Andrew đã diễn đạt thuật ngữ này) và kiểm tra xem chúng có tương đương.

Bây giờ hãy nhìn vào những gì cppreference nói về std::same_as:

std::same_as<T, U>phụ std::same_as<U, T>và ngược lại.

Về cơ bản, đây là mối quan hệ "nếu và chỉ-nếu": chúng ngụ ý lẫn nhau. (Tương đương logic)

Phỏng đoán của tôi là ở đây các ràng buộc nguyên tử std::is_same_v<T, U>. Cách trình biên dịch xử lý std::is_same_vcó thể khiến họ suy nghĩ std::is_same_v<T, U>std::is_same_v<U, T>như hai ràng buộc khác nhau (chúng là các thực thể khác nhau!). Vì vậy, nếu bạn thực hiện std::same_aschỉ sử dụng một trong số họ:

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

Sau đó std::same_as<T, U>std::same_as<U, T>sẽ "nổ tung" với các ràng buộc nguyên tử khác nhau và trở nên không tương đương.

Vâng, tại sao trình biên dịch quan tâm?

Xem xét ví dụ này :

#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;
}

Lý tưởng nhất, my_same_as<T, U> && std::integral<T>subsumes my_same_as<U, T>; do đó, trình biên dịch nên chọn chuyên môn mẫu thứ hai, ngoại trừ ... nó không: trình biên dịch phát ra lỗi error: call of overloaded 'foo(int, int)' is ambiguous.

Lý do đằng sau điều này là vì my_same_as<U, T>my_same_as<T, U>không phụ thuộc lẫn nhau, my_same_as<T, U> && std::integral<T>my_same_as<U, T>trở nên không thể so sánh được (trên tập các ràng buộc được sắp xếp một phần theo quan hệ của sự sụt giảm).

Tuy nhiên, nếu bạn thay thế

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

với

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

Mã biên dịch.


same_as <T, U> và same_as <U, T> cũng có thể là các mâu thuẫn nguyên tử khác nhau nhưng kết quả của chúng vẫn giống nhau. Tại sao trình biên dịch quan tâm rất nhiều về việc định nghĩa same_as như hai ràng buộc nguyên tử khác nhau mà theo quan điểm logic là giống nhau?
dùng7769147

2
Trình biên dịch được yêu cầu xem xét bất kỳ hai biểu thức nào là khác biệt cho việc loại bỏ ràng buộc, nhưng nó có thể xem xét các đối số theo chúng theo cách rõ ràng. Vì vậy, không chỉ làm chúng ta cần cả hai chiều (để nó không quan trọng, trong đó đặt họ tên khi so sánh các ràng buộc), chúng ta cũng cần SameHelper: nó làm cho hai công dụng của is_same_vxuất phát từ cùng một biểu thức.
Davis Herring

@ user7769147 Xem câu trả lời cập nhật.
Rin Kaenbyou

1
Có vẻ như sự khôn ngoan thông thường là sai về bình đẳng khái niệm. Không giống như các mẫu is_same<T, U>giống hệt nhau is_same<U, T>, hai ràng buộc nguyên tử không được coi là giống hệt nhau trừ khi chúng cũng được hình thành từ cùng một biểu thức. Do đó cần cả hai.
AndyG

Thế còn are_same_as? template<typename T, typename U0, typename... Un> concept are_same_as = SameAs<T, U0> && (SameAs<T, Un> && ...);sẽ thất bại trong một số trường hợp. Ví dụ: are_same_as<T, U, int>sẽ tương đương are_same_as<T, int, U>nhưng khôngare_same_as<U, T, int>
user7769147

2

std::is_same được định nghĩa là đúng khi và chỉ khi:

Tên T và U cùng loại với cùng tiêu chuẩn cv

Theo như tôi biết, tiêu chuẩn không định nghĩa nghĩa của "cùng loại", nhưng trong ngôn ngữ tự nhiên và logic "giống nhau" là một mối quan hệ tương đương và do đó là giao hoán.

Với giả định này, mà tôi gán cho, is_same_v<T, U> && is_same_v<U, V>thực sự sẽ là dư thừa. Nhưng same_­askhông được quy định trong điều khoản của is_same_v; đó chỉ là để giải thích.

Việc kiểm tra rõ ràng cho cả hai cho phép thực hiện same-as-implđể thỏa mãn same_­asmà không cần giao hoán. Chỉ định nó theo cách này mô tả chính xác cách thức hoạt động của khái niệm mà không hạn chế cách thực hiện.

Chính xác tại sao phương pháp này được chọn thay vì chỉ định về mặt is_same_v, tôi không biết. Một lợi thế của phương pháp được lựa chọn là có thể cho rằng hai định nghĩa được ghép lại. Người này không phụ thuộc vào người kia.


2
Tôi đồng ý với bạn, nhưng cuộc tranh luận cuối cùng này là một chút căng thẳng. Đối với tôi, nó có vẻ như: "Này, tôi có thành phần có thể tái sử dụng này cho tôi biết hai loại có giống nhau hay không. Bây giờ tôi có thành phần khác này cần biết liệu các loại có giống nhau không, nhưng, thay vì sử dụng lại thành phần trước đó của tôi , Tôi sẽ chỉ tạo ra một giải pháp đặc biệt cho trường hợp này. Bây giờ tôi đã 'tách' chàng trai cần định nghĩa về sự bình đẳng từ chàng trai có định nghĩa về sự bình đẳng. Yay! "
Cássio Renan

1
@ CássioRenan Chắc chắn. Như tôi đã nói, tôi không biết tại sao, đó chỉ là lý do tốt nhất mà tôi có thể đưa ra. Các tác giả có thể có một lý do tốt hơn.
eerorika
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.