ทำไมต้องใช้“ b <a? a: b” แทนที่จะเป็น“ a <b? b: a” เพื่อสร้างเทมเพลตสูงสุด?


154

เทมเพลต C ++ - คู่มือฉบับสมบูรณ์ฉบับที่ 2แนะนำเทมเพลตสูงสุด :

template<typename T>
T max (T a, T b)
{
  // if b < a then yield a else yield b
  return  b < a ? a : b;
}

และมันอธิบายการใช้“b < a ? a : b”แทน“a < b ? b : a”:

โปรดทราบว่าเทมเพลตสูงสุด () ตาม [StepanovNotes] จะส่งคืนโดยเจตนา“ b <a? a: b” แทนที่จะเป็น“ a <b? b: a” เพื่อให้แน่ใจว่าฟังก์ชั่นทำงานอย่างถูกต้องแม้ว่าค่าทั้งสองจะเท่ากัน แต่ไม่เท่ากัน

วิธีทำความเข้าใจ " even if the two values are equivalent but not equal." “a < b ? b : a”ดูเหมือนจะมีผลเหมือนกันสำหรับฉัน


8
หน้าตาผิดที่ฉัน ... คำตอบทั้งสองมีความ "ถูกต้อง" แต่ถ้าaและbเป็นเทียบเท่าแล้ว!(a < b) && !(b < a)เป็นความจริงดังนั้นa < bและb < aมีทั้งที่เป็นเท็จดังนั้นในb < a ? a : b, bจะถูกส่งกลับซึ่งไม่ได้เป็นสิ่งที่คุณต้องการ ... a < b ? b : aคุณต้องการ
Holt

1
คุณสามารถแยกแยะความแตกต่างระหว่างการเทียบเท่าaและbกับstd::addressofet อัล
Caleth

14
หากคุณทำa = max(a, b);(ซ้ำ ๆ ) คุณอาจไม่ต้องการแทนที่aโดยไม่จำเป็น
โบเพอร์สัน

2
BTW เทมเพลตนี้ควรใช้พารามิเตอร์โดยการอ้างอิง const และส่งกลับโดยอ้างอิง const มิฉะนั้นคุณจะทำสำเนาไร้ประโยชน์พวง (และคุณจะแทนที่aด้วยสำเนาa)
Holt

3
@Caleth: ประเภทบัญญัติซึ่งมีทั้งความเสมอภาคและความเท่าเทียมกันคือ CaseInsensitiveString สำหรับประเภทนั้นทั้ง <A หรือ A <a แต่std::addressofไม่เกี่ยวข้อง ในความเป็นจริงสำหรับให้เรารู้อยู่แล้วว่าT max(T a, T b) addressof(a) != addressof(b)
MSalters

คำตอบ:


150

std::max(a, b)ถูกระบุแน่นอนว่าจะส่งคืนaเมื่อทั้งสองค่าเท่ากัน

นั่นถือว่าเป็นความผิดพลาดของStepanovและคนอื่น ๆ เพราะมันทำลายทรัพย์สินที่มีประโยชน์ที่ให้ไว้aและbคุณสามารถเรียงลำดับได้ด้วย{min(a, b), max(a, b)}; เพื่อที่คุณจะต้องการmax(a, b)กลับbเมื่อมีข้อโต้แย้งที่เทียบเท่า


48
จากลิงค์นั้น "มันยากสำหรับฉันที่จะตำหนิคนที่ทำเช่นนั้น: หลังจากทั้งหมดพวกเขาเพียงทำตามข้อกำหนดมาตรฐาน C ++ ของmax ที่เขียนโดยฉันฉันใช้เวลาหลายปีกว่าจะเห็นว่าฉันเข้าใจผิด" - ว้าว!
Jack Aidley

23
คุณทำ{min(a, b), max(b, a)}ไม่ได้เหรอ
กัปตันแมน

12
@CaptainMan: ใช่ แต่ก็ยังไม่ค่อยชัดเจน ฉันจะยืนยันว่ามันทำให้รู้สึกตรรกะที่max(a,b)จะกลับมาถ้าและเพียงถ้าmin(a,b)ผลตอบแทน B และในทางกลับกันเพื่อให้พวกเขากลับของแต่ละอื่น ๆ และ (เรียงลำดับ) ชุดอยู่เสมอเท่ากับ{min(a,b), max(a,b)} {a,b}
Jack Aidley

5
@ jpmc26: หากมีตัวอย่างเช่นการเรียงลำดับรายการเหตุการณ์ตามเวลาคุณไม่จำเป็นต้องสนใจว่าการดำเนินการเรียงลำดับนั้นมีความเสถียรในการดูแลเกี่ยวกับการให้แต่ละเหตุการณ์ที่ปรากฏครั้งเดียวในอินพุตหรือไม่เช่นเดียวกันปรากฏขึ้นครั้งเดียวในเอาต์พุต การดำเนินการบางอย่าง (เช่นการค้นหาและกำจัดเหตุการณ์ที่ซ้ำกัน) อาจต้องใช้การสั่งซื้อที่สมบูรณ์ แต่ในหลายกรณีมันอาจเป็นที่ยอมรับได้ที่จะแสดงรายการเหตุการณ์ที่เกิดขึ้นพร้อมกันตามลำดับโดยพลการ แต่ไม่ได้ทำซ้ำหรือละเว้น
supercat

2
@supercat การใช้minและmaxสิ่งใด ๆ ยกเว้นการประทับเวลา (คีย์การเรียงลำดับ) ในสถานการณ์นั้นไม่สมเหตุสมผล เหตุการณ์ (วัตถุ) นั้นไม่ควรนำมาเปรียบเทียบกันได้หากความเท่าเทียมกันไม่ได้หมายถึงการสับเปลี่ยนกันได้ วิธีเดียวที่{min(a, b), max(a, b)}จะทำให้การใด ๆความรู้สึกที่เป็นกลไกในการจัดเรียงคือถ้าวัตถุที่สามารถใช้แทนกัน
jpmc26

62

คำตอบนี้อธิบายว่าทำไมรหัสที่กำหนดนั้นผิดจากมุมมองมาตรฐาน C ++ แต่ไม่อยู่ในบริบท

ดูคำตอบของ @ TCสำหรับคำอธิบายเชิงบริบท


มาตรฐานกำหนดstd::max(a, b)ดังนี้[alg.min.max] (การเน้นคือของฉัน):

template<class T> constexpr const T& max(const T& a, const T& b);

ต้องการ : Type T คือ LessThanComableable (ตารางที่ 18)

คืนค่า : ค่าที่มากกว่า

ข้อสังเกต : ส่งคืนอาร์กิวเมนต์แรกเมื่ออาร์กิวเมนต์มีค่าเท่ากัน

วิธีเทียบเท่านี่เป็นที่ที่!(a < b) && !(b < a)ถูกtrue [alg.sorting # 7]

โดยเฉพาะอย่างยิ่งถ้าaและbเทียบเท่าทั้งสองa < bและb < aมีfalseเพื่อให้คุ้มค่าทางด้านขวาของ:จะถูกส่งกลับในผู้ประกอบการที่มีเงื่อนไขดังนั้นaจะต้องมีทางด้านขวาเพื่อ:

a < b ? b : a

... ดูเหมือนจะเป็นคำตอบที่ถูกต้อง นี้เป็นรุ่นที่ใช้โดยlibstdc ++และlibc ++

ข้อมูลในใบเสนอราคาของคุณดูเหมือนจะผิดตามมาตรฐานปัจจุบัน แต่บริบทที่กำหนดไว้อาจแตกต่างกัน


4
ลิงค์ Godboltที่อธิบายปัญหา (ขอบคุณ @songyuanyao สำหรับคำจำกัดความของX)
โฮลท์

1
@JackAidley ฉันได้แก้ไขคำตอบเพื่อระบุว่าการใช้เหตุผลกำหนดเป้าหมายมาตรฐานปัจจุบัน
Holt

@codekaizer ฉันเป็นจริงหมายถึง"ถ้าเรากำหนด equiv (A, B) ในฐานะ! comp (A, B) &&! comp (ขก)" ฉันได้เปลี่ยนลิงค์ไปเป็นเครื่องหมายคำพูดที่ดีกว่า (3 บรรทัดด้านล่างในมาตรฐาน ... )
โฮลท์

1
ไม่มีใครแปลกใจที่ได้กล่าวถึงจุดลอยที่a<bและb<aทั้งสองสามารถเป็นเท็จเพราะพวกเขากำลังเรียงลำดับ (หนึ่งหรือทั้งสองน่านจึง==เป็นเท็จเกินไป) ที่อาจถูกมองว่าเป็นความเท่าเทียม ที่เกี่ยวข้องอย่างอิสระ: x86 ของการดำเนินการเรียนการสอนmaxsd a, b a = max(b,a) = b < a ? a : b( คำสั่งที่ให้ FP ขั้นต่ำแบบไม่มีสาขาและสูงสุดใน x86 คืออะไร ) การเรียนการสอนช่วยให้ตัวถูกดำเนินการแหล่งที่มา (ที่สอง) ใน unordered ดังนั้นวนรอบอาร์เรย์จะให้คุณน่านถ้ามี NaN ใด ๆ แต่max_seen = max(max_seen, a[i])จะไม่สนใจ NaNs
Peter Cordes


21

ประเด็นคือสิ่งที่ควรจะกลับเมื่อพวกเขาเท่ากัน; std::maxต้องส่งคืนa(เช่นอาร์กิวเมนต์แรก) สำหรับกรณีนี้

aถ้าพวกเขาจะเทียบเท่าผลตอบแทน

ดังนั้นa < b ? b : aควรใช้; ในทางกลับกันb < a ? a : b;จะกลับมาbอย่างไม่ถูกต้อง

(ดังที่ @ โฮลท์พูดข้อความอ้างอิงนั้นตรงกันข้าม)

"ค่าทั้งสองมีค่าเท่ากัน แต่ไม่เท่ากัน" หมายความว่าพวกเขามีค่าเท่ากันเมื่อทำการเปรียบเทียบ แต่พวกเขาย้ายเป็นวัตถุที่แตกต่างกันในด้านอื่น ๆ

เช่น

struct X { int a; int b; };
bool operator< (X lhs, X rhs) { return lhs.a < rhs.a; }
X x1 {0, 1};
X x2 {0, 2};
auto x3 = std::max(x1, x2); // it's guaranteed that an X which cantains {0, 1} is returned

1
คุณช่วยอธิบายรายละเอียดเกี่ยวกับสาเหตุที่std::max(a, b)ต้องกลับคืนได้aไหมถ้าaและbเท่ากัน?
眠りネロク

4
@ネロク- มันเป็นเพียงแค่การเลือกโดยพลการในส่วนหนึ่งของมาตรฐานของ แม้ว่ามันจะเป็นที่ถกเถียงกันอยู่บ้างถ้ามันดี
StoryTeller - Unslander Monica

มันเป็นเพียงฉันหรือเป็นสิ่งที่ขัดแย้งกับคำถามหรือไม่ ถ้าaและbเทียบเท่า!(a < b) && !(b < a)มันเป็นจริงดังนั้นa < bและb < aเท็จดังนั้น ... ?
Holt

2
@ ネロクฉันคิดว่ามาตรฐานแค่ต้องการให้มันเป็นตัวกำหนด; เมื่อพวกเขากำลังเท่าที่ควรจะกลับ
songyuanyao

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