Bộ so sánh trong suốt là gì?


106

Trong C ++ 14, các vùng chứa liên kết dường như đã thay đổi từ C ++ 11 - [Associateative.reqmts] / 13 cho biết:

Các mẫu hàm thành viên find, count, lower_bound, upper_bound, và equal_rangesẽ không tham gia vào giải quyết tình trạng quá tải trừ loại Compare::is_transparenttồn tại.

Mục đích của việc làm cho một bộ so sánh "trong suốt" là gì?

C ++ 14 cũng cung cấp các mẫu thư viện như sau:

template <class T = void> struct less {
    constexpr bool operator()(const T& x, const T& y) const;
    typedef T first_argument_type;
    typedef T second_argument_type;
    typedef bool result_type;
};

template <> struct less<void> {
    template <class T, class U> auto operator()(T&& t, U&& u) const
    -> decltype(std::forward<T>(t) < std::forward<U>(u));
    typedef *unspecified* is_transparent;
};

Vì vậy, ví dụ, std::set<T, std::less<T>>sẽ không có một bộ so sánh minh bạch, nhưng std::set<T, std::less<>> sẽ có một bộ so sánh .

Điều này giải quyết vấn đề gì và điều này có thay đổi cách hoạt động của các vùng chứa tiêu chuẩn không? Ví dụ, các thông số mẫu của std::setvẫn còn Key, Compare = std::less<Key>, ..., vì vậy không những thiết lập mặc định của nó mất find, countvv thành viên?


Ví dụ, hãy xem mô tả cppreference này . Và tôi cảm thấy ngu ngốc bây giờ, vì tôi ghi nhận từ "chức năng thành viên mẫu " ...
Kerrek SB


cppreference cũng có một tin đồn
Cubbi

Câu trả lời:


60

Vấn đề này giải quyết vấn đề gì,

Xem câu trả lời của Dietmarcâu trả lời của nhãn hiệu lại .

và điều này có thay đổi cách hoạt động của các thùng chứa tiêu chuẩn không?

Không, không phải theo mặc định.

Quá tải mẫu hàm thành viên mới, findv.v. cho phép bạn sử dụng một loại có thể so sánh với khóa của vùng chứa, thay vì sử dụng chính loại khóa. Xem N3465 của Joaquín Mª López Muñoz để biết cơ sở lý luận và một đề xuất chi tiết, được viết cẩn thận để thêm tính năng này.

Tại cuộc họp Bristol, LWG đã đồng ý rằng tính năng tra cứu không đồng nhất là hữu ích và đáng mong đợi, nhưng chúng tôi không thể chắc chắn rằng đề xuất của Joaquín sẽ an toàn trong mọi trường hợp. Đề xuất N3465 sẽ gây ra sự cố nghiêm trọng cho một số chương trình (xem phần Tác động đến mã hiện có ). Joaquín đã chuẩn bị một đề xuất dự thảo cập nhật với một số triển khai thay thế với các đánh đổi khác nhau, điều này rất hữu ích giúp LWG hiểu được những ưu và nhược điểm, nhưng tất cả đều có nguy cơ phá vỡ một số chương trình theo một cách nào đó nên không có sự đồng thuận để thêm tính năng. Chúng tôi quyết định rằng mặc dù sẽ không an toàn nếu thêm tính năng này vô điều kiện, nhưng sẽ an toàn nếu tính năng bị tắt theo mặc định và chỉ "chọn tham gia".

Sự khác biệt chính của đề xuất N3657 (là bản sửa đổi vào phút cuối của tôi và STL dựa trên N3465 và bản thảo chưa được xuất bản sau đó của Joaquín) là thêm is_transparentloại làm giao thức có thể được sử dụng để chọn tham gia vào chức năng mới.

Nếu bạn không sử dụng "functor trong suốt" (tức là một cái xác định một is_transparentloại) thì các vùng chứa sẽ hoạt động giống như chúng đã luôn làm và đó vẫn là mặc định.

Nếu bạn chọn sử dụng std::less<>(mới cho C ++ 14) hoặc một loại "chức năng trong suốt" khác thì bạn sẽ có được chức năng mới.

Sử dụng std::less<>dễ dàng với các mẫu bí danh:

template<typename T, typename Cmp = std::less<>, typename Alloc = std::allocator<T>>
  using set = std::set<T, Cmp, Alloc>;

Tên is_transparentnày xuất phát từ N3421 của STL đã thêm "toán tử kim cương" vào C ++ 14. Một "hàm hàm trong suốt" là một hàm chấp nhận bất kỳ loại đối số nào (không nhất thiết phải giống nhau) và chỉ cần chuyển tiếp các đối số đó tới một toán tử khác. Một hàm như vậy sẽ trở thành chính xác những gì bạn muốn để tra cứu không đồng nhất trong các vùng chứa kết hợp, vì vậy loại is_transparentnày đã được thêm vào tất cả các toán tử kim cương và được sử dụng làm loại thẻ để chỉ ra chức năng mới sẽ được bật trong các vùng chứa liên kết. Về mặt kỹ thuật, các vùng chứa không cần "bộ điều khiển trong suốt", chỉ cần một bộ hỗ trợ gọi nó với các loại không đồng nhất (ví dụ: pointer_comploại trong https://stackoverflow.com/a/18940595/981959 không trong suốt theo định nghĩa của STL,pointer_comp::is_transparentcho phép nó được sử dụng để giải quyết vấn đề). Nếu bạn chỉ tra cứu std::set<T, C>bằng các khóa loại Thoặc intsau đó Cchỉ cần có thể gọi được với các đối số loại Tint(theo một trong hai thứ tự), thì nó không cần phải thực sự minh bạch. Chúng tôi đã sử dụng tên đó một phần vì chúng tôi không thể tìm ra một cái tên tốt hơn (tôi sẽ thích hơn is_polymorphicvì những bộ chức năng như vậy sử dụng đa hình tĩnh, nhưng đã có một std::is_polymorphicđặc điểm kiểu đề cập đến đa hình động).


3
Này, bạn có phải là người mà STL đã nói, "Tất nhiên bạn có thể suy luận đối số mẫu trong đầu của bạn" trong cuộc nói chuyện mà lenstar liên kết với?
Kerrek SB

10
Không, tôi không có ở đó, nhưng có những người với nhiều phù hợp với trình biên dịch trong đầu họ hơn tôi có :)
Jonathan Wakely

Tôi đoán "toán tử kim cương" đề cập đến <>trong đề xuất được liên kết, nhưng đề xuất đó không giới thiệu <>- đó là cú pháp hiện có cho danh sách tham số mẫu trống. "Chức năng toán tử kim cương" sẽ ít khó hiểu hơn một chút.
Qwertie

33

Trong C ++ 11 không có thành viên mẫu find(), lower_bound()vv Đó là, không có gì bị mất bởi sự thay đổi này. Các mẫu thành viên được giới thiệu với n3657 để cho phép các khóa không đồng nhất được sử dụng với các vùng chứa liên kết. Tôi không thấy bất kỳ ví dụ cụ thể nào hữu ích ngoại trừ ví dụ tốt và xấu!

Việc is_transparentsử dụng nhằm mục đích tránh những chuyển đổi không mong muốn. Nếu các mẫu thành viên không bị giới hạn, mã hiện tại có thể chuyển trực tiếp qua các đối tượng mà lẽ ra sẽ được chuyển đổi mà không có các mẫu thành viên. Ví dụ use-case từ n3657 đang định vị một đối tượng trong một std::set<std::string>chuỗi ký tự bằng cách sử dụng: với định nghĩa C ++ 11, một std::stringđối tượng được xây dựng khi truyền một chuỗi ký tự cho hàm thành viên tương ứng. Với sự thay đổi, có thể sử dụng trực tiếp chuỗi ký tự. Nếu đối tượng hàm so sánh cơ bản được triển khai độc quyền về mặt điều std::stringđó là không tốt vì bây giờ a std::stringsẽ được tạo cho mỗi so sánh. Mặt khác, nếu đối tượng hàm so sánh cơ bản có thể nhậnstd::string và một chuỗi ký tự, có thể tránh việc xây dựng một đối tượng tạm thời.

Kiểu lồng nhau is_transparenttrong đối tượng hàm so sánh cung cấp một cách để chỉ định xem có nên sử dụng hàm thành viên mẫu hay không: nếu đối tượng hàm so sánh có thể xử lý các đối số không đồng nhất, nó định nghĩa kiểu này để chỉ ra rằng nó có thể xử lý các đối số khác nhau một cách hiệu quả. Ví dụ, các đối tượng hàm toán tử mới chỉ ủy quyền operator<()và tuyên bố là trong suốt. Điều đó, ít nhất, hoạt động std::stringmà đã quá tải ít hơn các toán tử lấy char const*làm đối số. Vì các đối tượng chức năng này cũng mới, nên ngay cả khi chúng làm sai (nghĩa là yêu cầu chuyển đổi cho một số loại) thì ít nhất, nó sẽ không phải là một thay đổi âm thầm dẫn đến giảm hiệu suất.


Cảm ơn - hãy xem nhận xét của tôi về câu hỏi khác: Bạn có nhận được hành vi minh bạch theo mặc định không?
Kerrek SB

8
@KerrekSB: hành vi trong suốt được kích hoạt khi is_transparentđược xác định trong đối tượng hàm so sánh theo 23.2.4 [Associateative.reqmts] đoạn 13. Các đối tượng hàm so sánh mặc định std::less<Key>theo 23.4.2 [Associateative.map.syn] và 23.4. 3 [liên kết.set.syn]. Theo 20.10.5 [so sánh] Đoạn 4 mẫu chung std::less<...>nào không định nghĩa một kiểu lồng nhau is_transparentnhưng std::less<void>chuyên môn thực hiện. Đó là, không, bạn không nhận được toán tử minh bạch theo mặc định.
Dietmar Kühl

Bạn có bất kỳ ý tưởng cho việc đặt tên? Ý tôi là tại sao is_transparent?
plasmacel

Bạn muốn một "ví dụ cụ thể về nơi điều này hữu ích"? Đây là trường hợp sử dụng của tôi
tán

19

Sau đây là tất cả copy-pasta từ n3657 .

H. Mục đích của việc làm cho một bộ so sánh "trong suốt" là gì?

A. Các hàm tra cứu vùng chứa kết hợp (find, low_bound, upper_bound, equal_range) chỉ nhận một đối số của key_type, yêu cầu người dùng phải xây dựng (ngầm định hoặc rõ ràng) một đối tượng của key_type để thực hiện việc tra cứu. Điều này có thể tốn kém, ví dụ: xây dựng một đối tượng lớn để tìm kiếm trong một tập hợp khi hàm so sánh chỉ xem xét một trường của đối tượng. Người dùng rất mong muốn có thể tìm kiếm bằng các kiểu khác có thể so sánh với key_type.

Q. Vấn đề này giải quyết vấn đề gì

A. LWG có những lo ngại về mã như sau:

std::set<std::string> s = /* ... */;
s.find("key");

Trong C ++ 11, điều này sẽ xây dựng một std :: string tạm thời và sau đó so sánh nó với các phần tử để tìm khóa.

Với sự thay đổi được đề xuất bởi N3465, hàm std :: set :: find () sẽ là một mẫu không bị giới hạn sẽ chuyển const char * qua hàm so sánh, std :: less, sẽ tạo một chuỗi std :: tạm thời cho mọi so sánh. LWG coi vấn đề hiệu suất này là một vấn đề nghiêm trọng. Hàm find () mẫu cũng sẽ ngăn việc tìm NULL trong vùng chứa con trỏ, điều này khiến mã hợp lệ trước đó không còn được biên dịch nữa, nhưng đây được coi là một vấn đề ít nghiêm trọng hơn so với hồi quy hiệu suất im lặng

Q. điều này có thay đổi cách các vùng chứa tiêu chuẩn hoạt động không

A. Đề xuất này sửa đổi các vùng chứa kết hợp trong và bằng cách nạp chồng các hàm thành viên tra cứu với các mẫu hàm thành viên. Không có thay đổi ngôn ngữ.

Q. do đó, tập hợp mặc định có mất các thành viên tìm, đếm, v.v.

Đ. Hầu như tất cả mã C ++ 11 hiện có không bị ảnh hưởng bởi vì các hàm thành viên không có mặt trừ khi các tính năng thư viện C ++ 14 mới được sử dụng làm hàm so sánh.

Để trích dẫn Yakk ,

Trong C ++ 14, std :: set :: find là một hàm mẫu nếu có tồn tại Compare :: is_transparent. Loại bạn nhập không cần phải là Khóa, chỉ cần tương đương dưới bộ so sánh của bạn.

và n3657,

Thêm đoạn 13 trong 23.2.4 [Associateative.reqmts]: Các mẫu hàm thành viên find, Lower_bound, upper_bound và equal_range sẽ không tham gia vào việc giải quyết quá tải trừ khi kiểu Compare :: is_transparent không tồn tại.

n3421 cung cấp một ví dụ về "Người điều hành hoạt động minh bạch" .

Các mã đầy đủ là ở đây .


1
std::set<std::string>thực sự được hưởng lợi từ "vượt qua char const *thông qua", hay bạn cần phải thực hiện một std::set<std::string, std::less<>>?
Kerrek SB

@Kerrek Tôi nghĩ rằng "vượt qua char const *" là vấn đề mà họ đang cố gắng tránh, nếu tôi không nhầm. Nhìn vào cách diễn đạt:With the change proposed by N3465 the std::set::find() function would be an unconstrained template which would pass the const char* through to the comparator function, std::less<std::string>, which would construct a std::string temporary for every comparison. The LWG considered this performance problem to be a serious issue.

Trích dẫn của bạn và của tôi đoạn 13 nói ngược lại: "trừ khi kiểu tồn tại / không tồn tại" ...?!
Kerrek SB

4
@KerrekSB, đó là lỗi của tôi, N3657 được cho là "tồn tại" nhưng tôi đã viết "không tồn tại" ... đó là một tờ giấy muộn được viết vào phút cuối. Tiêu chuẩn dự thảo là đúng.
Jonathan Wakely

3
Vâng, nó có thể là rõ ràng hơn để trích dẫn những gì tôi có nghĩa là để nói không phải những gì tôi thực sự nói vào thời điểm đó :)
Jonathan Wakely

7

Stephan T Lavavej nói về các vấn đề trong đó trình biên dịch tiếp tục tạo ra các khoảng thời gian tạm thời và cách đề xuất của anh ấy về các chức năng toán tử minh bạch sẽ giải quyết vấn đề này trong c ++ 1y

GoingNative 2013 - Không trợ giúp Trình biên dịch (vào khoảng mốc giờ)

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.