ตัวเปรียบเทียบแบบโปร่งใสคืออะไร?


106

ใน C ++ 14 ดูเหมือนว่า Associative container จะเปลี่ยนไปจาก C ++ 11 - [Associative.reqmts] / 13 พูดว่า:

แม่แบบฟังก์ชันสมาชิกfind, count, lower_bound, upper_boundและequal_rangeจะได้มีส่วนร่วมในการแก้ปัญหาเกินเว้นแต่ชนิดCompare::is_transparentที่มีอยู่

จุดประสงค์ของการสร้างตัวเปรียบเทียบ "โปร่งใส" คืออะไร?

C ++ 14 ยังมีเทมเพลตไลบรารีเช่นนี้:

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

ตัวอย่างเช่นstd::set<T, std::less<T>>จะไม่มีตัวเปรียบเทียบแบบโปร่งใส แต่std::set<T, std::less<>> จะมีตัวเปรียบเทียบ

ปัญหานี้ช่วยแก้ปัญหาอะไรและสิ่งนี้เปลี่ยนวิธีการทำงานของคอนเทนเนอร์มาตรฐานหรือไม่ ยกตัวอย่างเช่นแม่แบบพารามิเตอร์ของstd::setยังคงอยู่Key, Compare = std::less<Key>, ...ดังนั้นไม่ชุดเริ่มต้นการสูญเสียของมันfind, countสมาชิก ฯลฯ ?


ตัวอย่างเช่นดูคำอธิบาย cppreferenceนี้ ตอนนี้ฉันรู้สึกโง่เพราะฉันสังเกตคำว่า " เทมเพลตฟังก์ชันสมาชิก" ...
Kerrek SB

5
อาจเกี่ยวข้อง: stackoverflow.com/questions/18939882/…

cppreference ยังมีการนำเสนอ en.cppreference.com/w/cpp/utility/functional/less_void
Cubbi

คำตอบ:


60

ปัญหานี้แก้ปัญหาอะไร

ดูคำตอบ Dietmar ของและคำตอบของ remyabel

และสิ่งนี้เปลี่ยนวิธีการทำงานของคอนเทนเนอร์มาตรฐานหรือไม่

ไม่ไม่ใช่โดยค่าเริ่มต้น

เทมเพลตฟังก์ชันสมาชิกใหม่มีการโอเวอร์โหลดfindฯลฯ ช่วยให้คุณสามารถใช้ประเภทที่เทียบเคียงได้กับคีย์ของคอนเทนเนอร์แทนที่จะใช้ประเภทคีย์เอง ดูN3465โดยJoaquínMªLópezMuñozสำหรับเหตุผลและข้อเสนอที่เขียนอย่างละเอียดและละเอียดเพื่อเพิ่มคุณสมบัตินี้

ในการประชุมบริสตอล LWG เห็นพ้องกันว่าคุณลักษณะการค้นหาที่หลากหลายมีประโยชน์และเป็นที่ต้องการ แต่เราไม่สามารถมั่นใจได้ว่าข้อเสนอของJoaquínจะปลอดภัยในทุกกรณี ข้อเสนอ N3465 อาจทำให้เกิดปัญหาร้ายแรงสำหรับบางโปรแกรม (ดูส่วนผลกระทบต่อโค้ดที่มีอยู่ ) Joaquínเตรียมร่างข้อเสนอที่อัปเดตพร้อมการใช้งานทางเลือกบางอย่างพร้อมการแลกเปลี่ยนที่แตกต่างกันซึ่งมีประโยชน์มากในการช่วยให้ LWG เข้าใจข้อดีข้อเสีย แต่พวกเขาทั้งหมดเสี่ยงที่จะทำลายบางโปรแกรมไม่ทางใดก็ทางหนึ่งดังนั้นจึงไม่มีฉันทามติที่จะเพิ่มคุณสมบัติ เราตัดสินใจว่าแม้ว่าจะไม่ปลอดภัยที่จะเพิ่มคุณลักษณะนี้โดยไม่มีเงื่อนไข แต่จะปลอดภัยหากปิดใช้งานโดยค่าเริ่มต้นและ "เลือกใช้" เท่านั้น

ความแตกต่างที่สำคัญของข้อเสนอN3657 (ซึ่งเป็นการแก้ไขในนาทีสุดท้ายโดยตัวฉันเองและ STL ตาม N3465และแบบร่างที่ไม่ได้เผยแพร่ในภายหลังโดยJoaquín) คือการเพิ่มis_transparentประเภทเป็นโปรโตคอลที่สามารถใช้เพื่อเลือกใช้ฟังก์ชันใหม่ได้

หากคุณไม่ใช้ "ฟังก์ชั่นโปร่งใส" (เช่นตัวที่กำหนดไฟล์ is_transparentประเภท) คอนเทนเนอร์จะทำงานเหมือนกับที่เคยทำมาตลอดและนั่นยังคงเป็นค่าเริ่มต้น

Iff คุณเลือกใช้ std::less<> (ซึ่งใหม่สำหรับ C ++ 14) หรือ "ฟังก์ชั่นโปร่งใส" ประเภทอื่นคุณจะได้รับฟังก์ชันใหม่

ใช้std::less<>งานง่ายด้วยเทมเพลตนามแฝง:

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

ชื่อis_transparentนี้มาจากN3421ของ STL ซึ่งเพิ่ม "ตัวดำเนินการเพชร" ให้กับ C ++ 14 "ตัวดำเนินการโปร่งใส" คือสิ่งที่ยอมรับประเภทอาร์กิวเมนต์ (ซึ่งไม่จำเป็นต้องเหมือนกัน) และเพียงแค่ส่งต่ออาร์กิวเมนต์เหล่านั้นไปยังตัวดำเนินการอื่น functor ดังกล่าวเป็นสิ่งที่คุณต้องการสำหรับการค้นหาที่แตกต่างกันในคอนเทนเนอร์แบบเชื่อมโยงดังนั้นประเภทis_transparentจึงถูกเพิ่มให้กับตัวดำเนินการเพชรทั้งหมดและใช้เป็นประเภทแท็กเพื่อระบุว่าควรเปิดใช้งานฟังก์ชันใหม่ในคอนเทนเนอร์ที่เชื่อมโยงกัน ในทางเทคนิคแล้วคอนเทนเนอร์ไม่จำเป็นต้องมี "ฟังก์ชั่นโปร่งใส" เพียงอันเดียวที่รองรับการเรียกด้วยประเภทที่แตกต่างกัน (เช่นpointer_compประเภทในhttps://stackoverflow.com/a/18940595/981959ไม่โปร่งใสตามคำจำกัดความของ STLpointer_comp::is_transparentช่วยให้สามารถใช้เพื่อแก้ปัญหาได้) หากคุณเคยค้นหาstd::set<T, C>ด้วยคีย์ประเภทTหรือintจากนั้นCจำเป็นต้องสามารถเรียกได้ด้วยอาร์กิวเมนต์ประเภทTและint(เรียงตามลำดับ) ไม่จำเป็นต้องโปร่งใสอย่างแท้จริง เราใช้ชื่อนั้นส่วนหนึ่งเป็นเพราะเราไม่สามารถหาชื่อที่ดีกว่านี้ได้ (ฉันต้องการis_polymorphicเพราะ functors ดังกล่าวใช้ความหลากหลายแบบคงที่ แต่มีstd::is_polymorphicลักษณะประเภทหนึ่งที่อ้างถึงความหลากหลายแบบไดนามิกอยู่แล้ว)


3
เฮ้คุณเป็นคนที่ STL พูดว่า "แน่นอนคุณสามารถหักอาร์กิวเมนต์แม่แบบในหัวของคุณ" ใน talk woolstar ที่เชื่อมโยงได้หรือไม่?
Kerrek SB

10
ไม่ฉันไม่ได้อยู่ที่นั่น แต่มีคนที่มีคอมไพเลอร์ที่สอดคล้องกันมากกว่าที่ฉันมี :)
Jonathan Wakely

ฉันเดาว่า "ตัวดำเนินการเพชร" อ้างถึง<>ในข้อเสนอที่เชื่อมโยง แต่ข้อเสนอนั้นไม่ได้แนะนำ<>- เป็นไวยากรณ์ที่มีอยู่สำหรับรายการพารามิเตอร์เทมเพลตว่าง "Diamond operator functors" คงจะสับสนไม่น้อย
Qwertie

33

ใน C ++ 11 มีไม่แม่สมาชิกfind(), lower_bound()ฯลฯ นั่นคือไม่มีอะไรจะสูญเสียไปจากการเปลี่ยนแปลงนี้ เทมเพลตสมาชิกถูกนำมาใช้กับ n3657 เพื่ออนุญาตให้ใช้คีย์ที่ต่างกันกับคอนเทนเนอร์ที่เชื่อมโยง ฉันไม่เห็นตัวอย่างที่เป็นรูปธรรมที่มีประโยชน์ยกเว้นตัวอย่างที่ดีและไม่ดี!

การis_transparentใช้งานมีวัตถุประสงค์เพื่อหลีกเลี่ยงการแปลงที่ไม่ต้องการ หากเทมเพลตสมาชิกไม่มีข้อ จำกัด โค้ดที่มีอยู่อาจส่งผ่านอ็อบเจ็กต์โดยตรงซึ่งจะถูกแปลงโดยไม่มีเทมเพลตสมาชิก ตัวอย่าง use-case จาก n3657 กำลังค้นหาอ็อบเจ็กต์ในการstd::set<std::string>ใช้สตริงลิเทอรัล: ด้วยนิยาม C ++ 11 std::stringอ็อบเจ็กต์ถูกสร้างขึ้นเมื่อส่งผ่านสตริงลิเทอรัลไปยังฟังก์ชันสมาชิกที่เกี่ยวข้อง ด้วยการเปลี่ยนแปลงนี้เป็นไปได้ที่จะใช้สตริงลิเทอรัลโดยตรง หากอ็อบเจ็กต์ฟังก์ชันการเปรียบเทียบพื้นฐานถูกนำมาใช้เฉพาะในแง่ของstd::stringสิ่งนั้นไม่ดีเนื่องจากตอนนี้ a และสตริงลิเทอรัลอาจหลีกเลี่ยงการสร้างอ็อบเจ็กต์ชั่วคราวstd::stringจะถูกสร้างขึ้นสำหรับการเปรียบเทียบแต่ละครั้ง ในทางกลับกันถ้าอ็อบเจ็กต์ฟังก์ชันการเปรียบเทียบพื้นฐานสามารถใช้ astd::string

is_transparentประเภทที่ซ้อนกันในอ็อบเจ็กต์ฟังก์ชันการเปรียบเทียบให้วิธีการระบุว่าควรใช้ฟังก์ชันสมาชิกเทมเพลตหรือไม่: หากอ็อบเจ็กต์ฟังก์ชันการเปรียบเทียบสามารถจัดการกับอาร์กิวเมนต์ที่แตกต่างกันได้จะกำหนดประเภทนี้เพื่อระบุว่าสามารถจัดการกับอาร์กิวเมนต์ที่แตกต่างกันได้อย่างมีประสิทธิภาพ ตัวอย่างเช่นอ็อบเจ็กต์ฟังก์ชันตัวดำเนินการใหม่จะมอบสิทธิ์operator<()และอ้างว่าโปร่งใส อย่างน้อยก็ใช้งานstd::stringได้ซึ่งมีโอเวอร์โหลดน้อยกว่าตัวดำเนินการที่ใช้char const*เป็นอาร์กิวเมนต์ เนื่องจากอ็อบเจ็กต์ฟังก์ชันเหล่านี้เป็นของใหม่แม้ว่าจะทำสิ่งที่ผิดพลาด (เช่นต้องมีการแปลงสำหรับบางประเภท) อย่างน้อยก็จะไม่เป็นการเปลี่ยนแปลงแบบเงียบที่ส่งผลให้ประสิทธิภาพลดลง


ขอบคุณ - ดูความคิดเห็นของฉันในคำถามอื่น ๆ : คุณมีพฤติกรรมโปร่งใสโดยค่าเริ่มต้นหรือไม่?
Kerrek SB

8
@KerrekSB: พฤติกรรมโปร่งใสจะเปิดใช้งานเมื่อis_transparentถูกกำหนดไว้ในอ็อบเจ็กต์ฟังก์ชันการเปรียบเทียบตาม 23.2.4 [Associative.reqmts] ย่อหน้าที่ 13 อ็อบเจ็กต์ฟังก์ชันเปรียบเทียบเริ่มต้นเป็นstd::less<Key>ไปตาม 23.4.2 [Associative.map.syn] และ 23.4 3 [Associative.set.syn]. ตาม 20.10.5 [การเปรียบเทียบ] ย่อหน้าที่ 4 แม่แบบทั่วไปสำหรับstd::less<...>ไม่ได้กำหนดประเภทที่ซ้อนกันis_transparentแต่มีstd::less<void>ความเชี่ยวชาญ นั่นคือไม่คุณจะไม่ได้รับตัวดำเนินการที่โปร่งใสโดยค่าเริ่มต้น
Dietmar Kühl

คุณมีความคิดในการตั้งชื่อหรือไม่? ฉันหมายความว่าทำไมis_transparent?
plasmacel

คุณต้องการ "ตัวอย่างที่เป็นรูปธรรมที่มีประโยชน์"? นี่คือกรณีการใช้งานของฉัน
spraff

19

ต่อไปนี้เป็นสำเนาพาสต้าจากn3657

ถาม: อะไรคือจุดประสงค์ของการทำให้ตัวเปรียบเทียบ "โปร่งใส"?

A. ฟังก์ชันการค้นหาคอนเทนเนอร์ที่เชื่อมโยงกัน (find, lower_bound, upper_bound, equal_range) ใช้อาร์กิวเมนต์ของ key_type เท่านั้นซึ่งกำหนดให้ผู้ใช้สร้างอ็อบเจกต์ของ key_type (โดยปริยายหรือโดยชัดแจ้ง) เพื่อทำการค้นหา ซึ่งอาจมีราคาแพงเช่นการสร้างวัตถุขนาดใหญ่เพื่อค้นหาในชุดเมื่อฟังก์ชันตัวเปรียบเทียบมองไปที่เขตข้อมูลของวัตถุเพียงช่องเดียว มีความปรารถนาดีในหมู่ผู้ใช้ที่จะสามารถค้นหาโดยใช้ประเภทอื่นซึ่งเทียบได้กับ key_type

คำถามนี้แก้ปัญหาอะไรได้บ้าง

A. LWG มีความกังวลเกี่ยวกับรหัสดังต่อไปนี้:

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

ใน C ++ 11 สิ่งนี้จะสร้าง std :: string ชั่วคราวจากนั้นเปรียบเทียบกับองค์ประกอบเพื่อค้นหาคีย์

ด้วยการเปลี่ยนแปลงที่เสนอโดย N3465 ฟังก์ชัน std :: set :: find () จะเป็นเทมเพลตที่ไม่มีข้อ จำกัด ซึ่งจะส่ง const char * ผ่านไปยังฟังก์ชันตัวเปรียบเทียบ std :: less ซึ่งจะสร้าง std :: string ชั่วคราวสำหรับ ทุกการเปรียบเทียบ LWG ถือว่าปัญหาด้านประสิทธิภาพนี้เป็นปัญหาร้ายแรง ฟังก์ชันค้นหาเทมเพลต () จะป้องกันการค้นหา NULL ในคอนเทนเนอร์ของพอยน์เตอร์ซึ่งทำให้โค้ดที่ถูกต้องก่อนหน้านี้ไม่สามารถคอมไพล์ได้อีกต่อไป แต่สิ่งนี้ถูกมองว่าเป็นปัญหาที่ร้ายแรงน้อยกว่าการถดถอยประสิทธิภาพการทำงานแบบไม่โต้ตอบ

ถามสิ่งนี้เปลี่ยนวิธีการทำงานของคอนเทนเนอร์มาตรฐานหรือไม่

ตอบข้อเสนอนี้ปรับเปลี่ยนคอนเทนเนอร์ที่เชื่อมโยงในและโดยการโอเวอร์โหลดฟังก์ชันสมาชิกการค้นหาด้วยเทมเพลตฟังก์ชันสมาชิก ไม่มีการเปลี่ยนแปลงภาษา

ถามดังนั้นชุดเริ่มต้นจึงสูญเสียการค้นหาจำนวนสมาชิก ฯลฯ

ตอบโค้ด C ++ 11 ที่มีอยู่เกือบทั้งหมดจะไม่ได้รับผลกระทบเนื่องจากไม่มีฟังก์ชันสมาชิกเว้นแต่จะใช้คุณลักษณะไลบรารี C ++ 14 ใหม่เป็นฟังก์ชันเปรียบเทียบ

ที่จะพูดYakk ,

ใน C ++ 14 std :: set :: find เป็นฟังก์ชันเทมเพลตหากมี Compare :: is_transparent อยู่ ประเภทที่คุณส่งผ่านไม่จำเป็นต้องเป็นคีย์ แต่เทียบเท่าภายใต้ตัวเปรียบเทียบของคุณ

และ n3657

เพิ่มวรรค 13 23.2.4 [associative.reqmts]: สมาชิกฟังก์ชันแม่หา lower_bound, upper_bound และ equal_range จะได้มีส่วนร่วมในการแก้ปัญหาเกินเว้นแต่ชนิดเปรียบเทียบ :: is_transparent ไม่อยู่ไม่อยู่

n3421ให้ตัวอย่างของ"ใส Operator Functors"

โค้ดเต็มรูปแบบอยู่ที่นี่


1
ไม่std::set<std::string>จริงได้รับประโยชน์จาก "ผ่านchar const *ผ่าน" หรือไม่หรือคุณต้องทำstd::set<std::string, std::less<>>?
Kerrek SB

@Kerrek ฉันคิดว่า "การผ่าน char const *" เป็นปัญหาที่พวกเขาพยายามหลีกเลี่ยงถ้าฉันไม่เข้าใจผิด ดูถ้อยคำ: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.

คำพูดของคุณและของฉันในย่อหน้าที่ 13 พูดในทางตรงกันข้าม: "เว้นแต่ว่ามีประเภทอยู่ / ไม่มีอยู่จริง" ... ?!
Kerrek SB

4
@KerrekSB นั่นเป็นความผิดของฉัน N3657 ควรจะบอกว่า "มีอยู่จริง" แต่ฉันเขียนว่า "ไม่มีอยู่จริง" ... มันเป็นกระดาษปลายสายที่เขียนในนาทีสุดท้าย ร่างมาตรฐานถูกต้อง
Jonathan Wakely

3
ใช่มันอาจจะชัดเจนกว่าที่จะพูดสิ่งที่ฉันตั้งใจจะพูดไม่ใช่สิ่งที่ฉันพูดจริงในเวลานั้น :)
Jonathan Wakely

7

Stephan T Lavavej พูดถึงปัญหาที่คอมไพเลอร์ยังคงสร้างชั่วคราวและข้อเสนอของเขาเกี่ยวกับตัวดำเนินการแบบโปร่งใสจะแก้ปัญหานี้ได้อย่างไรใน c ++ 1y

GoingNative 2013 - อย่าช่วยคอมไพเลอร์ (ประมาณเครื่องหมายชั่วโมง)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.