Làm cách nào để triển khai dạng tổng quát của std :: same_as (nghĩa là có nhiều hơn hai tham số loại) không theo thứ tự tham số?


8

Lý lịch

Chúng tôi biết rằng khái niệm std::same_asnày là bất khả tri theo thứ tự (nói cách khác, đối xứng): std::same_as<T, U>tương đương với std::same_as<U, T>( câu hỏi liên quan ). Trong câu hỏi này, tôi muốn thực hiện một cái gì đó tổng quát hơn: template <typename ... Types> concept same_are = ...kiểm tra xem các loại trong gói Typescó bằng nhau không.

Nỗ lực của tôi

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

template <typename T, typename... Others>
concept same_with_others = (... && std::same_as<T, Others>);

template <typename... Types>
concept are_same = (... && same_with_others<Types, Types...>);

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

// Note the order <U, T> is intentional
template< class T, class U> requires (are_same<U, T> && std::integral<T>)
void foo(T a, U b) {
    std::cout << "Integral" << std::endl;
}

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

(Ý định của tôi ở đây là liệt kê mọi cặp loại có thể có trong gói)

Thật không may, mã này sẽ không được biên dịch , với trình biên dịch phàn nàn rằng cuộc gọi đến foo(int, int)không rõ ràng. Tôi tin rằng nó xem xét are_same<U, T>are_same<T, U>như không tương đương. Tôi muốn biết tại sao mã bị lỗi làm thế nào tôi có thể sửa nó (để trình biên dịch coi chúng là tương đương)?


Ruột của tôi nói với tôi rằng nó sẽ cần một người trợ giúp thực thi same_with_otherstrong mọi hoán vị có thể có của các loại.
Người kể chuyện - Unslander Monica

Tôi không hoàn toàn chắc chắn nếu tôi hiểu bạn chính xác. Bạn muốn kiểm tra xem tất cả các loại ... Typesđều giống nhau? Có lẽ std :: kết hợp có thể giúp bạn. Có một ví dụ ở dưới cùng của trang trông giống với cách tiếp cận của bạn.
churill

@ StoryTeller-UnslanderMonica Nhưng tôi đã liệt kê tất cả các loại sắp xếp có thể có trong gói. Như vậy vẫn chưa đủ? Hoặc trình biên dịch không thể xác định sự tương đương của nếp gấp mà không có loại bê tông nào?
Rin Kaenbyou

@churill Ý tôi là thực hiện điều này trong các khái niệm và thứ tự tham số cần được chăm sóc đặc biệt trong các khái niệm.
Rin Kaenbyou

Tôi không chắc chắn, do đó nó chỉ là một cảm giác ruột. Có thể các nhà phát triển GCC chưa chắc chắn. Cũng có thể họ chưa thực hiện nó đầy đủ.
Người kể chuyện - Unslander Monica

Câu trả lời:


5

Vấn đề là, với khái niệm này:

template <typename T, typename... Others>
concept are_same = (... && std::same_as<T, Others>);

Có phải đó là hình thức chuẩn hóa của khái niệm này là ... chính xác đó. Chúng ta không thể "mở ra" điều này (không có gì để làm) và các quy tắc hiện tại không bình thường hóa thông qua "các phần" của một khái niệm.

Nói cách khác, những gì bạn cần để làm việc này là để khái niệm của bạn bình thường hóa thành:

... && (same-as-impl<T, U> && same-as-impl<U, T>)

vào:

... && (is_same_v<T, U> && is_same_v<U, T>)

Và xem xét một &&ràng buộc biểu thức gấp để loại bỏ một ràng buộc biểu thức gấp khác &&nếu ràng buộc cơ bản của nó làm giảm ràng buộc cơ bản của các ràng buộc khác. Nếu chúng tôi có quy tắc đó, điều đó sẽ làm cho ví dụ của bạn hoạt động.

Có thể có thể thêm điều này trong tương lai - nhưng mối quan tâm xung quanh các quy tắc phụ là chúng tôi không muốn yêu cầu trình biên dịch đi ra ngoài và thực hiện một bộ giải SAT đầy đủ để kiểm tra mức độ ràng buộc. Điều này dường như không làm cho nó phức tạp hơn nhiều (chúng tôi thực sự chỉ cần thêm &&||quy tắc thông qua các biểu thức gấp), nhưng tôi thực sự không có ý tưởng.

Tuy nhiên, xin lưu ý rằng ngay cả khi chúng ta có loại trợ cấp biểu thức gấp này, are_same<T, U>vẫn sẽ không giảm std::same_as<T, U>. Nó sẽ chỉ chìm are_same<U, T>. Tôi không chắc chắn nếu điều này thậm chí có thể.


2
Điều đáng ngạc nhiên với tôi là điều này không hiệu quả. Điều hợp lý là chỉ các khái niệm có thể được giảm bớt. Tuy nhiên, theo tôi, việc (... && C<T>)không bao gồm khái niệm C<T> này sẽ khiến nhiều người dùng ngạc nhiên.
metalfox

@metalfox: Từ việc đọc bình thường hóa của tôi, ví dụ của bạn sẽ ổn (sử dụng ràng buộc hoạt động rõ ràng mặc dù Demo ).
Jarod42

@ Jarod42 Những gì bạn đã viết và những gì metalfox đã viết không giống nhau - sự khác biệt là những gì metalfox đang nói đến.
Barry

@ Jarod42 Có, điều đó hoạt động vì các khái niệm tham gia vào hạn chế ràng buộc và bạn đã cụ thể hóa biểu thức gấp (liên quan đến các khái niệm) thành một khái niệm duy nhất. Thật không may, như bạn đã nêu trong câu trả lời của mình, các biểu thức gấp không được chuẩn hóa thành các khái niệm mà chúng được tạo ra. Điều này cũng không hoạt động: godbolt.org/z/pjmKxR
metalfox

Tôi có thể có hiểu lầm Constraint_normalization sau đó: - / Tôi hiểu ((fold1<Ts> && ...) && (fold2<Ts> &&...))kết hợp của (fold1<Ts> && ...)(fold2<Ts> && ...)trong khi nó là nguyên tử.
Jarod42

5

Từ cppreference.com Constraint_n normalization

Dạng bình thường của bất kỳ biểu thức E nào khác là ràng buộc nguyên tử có biểu thức là E và ánh xạ tham số là ánh xạ định danh. Điều này bao gồm tất cả các biểu thức gấp, ngay cả những biểu thức gấp trên && hoặc || khai thác.

Vì thế

template <typename... Types>
concept are_same = (... && same_with_others<Types, Types...>);

là "nguyên tử".

Vì vậy, thực sự are_same<U, T>are_same<T, U>không tương đương.

Tôi không thấy cách triển khai nó :-(

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.