Mengapa menggunakan "b <a? a: b "bukannya" a <b? b: a ”untuk mengimplementasikan max template?

154

C ++ Templates - Panduan Lengkap, Edisi ke-2 memperkenalkan max template:

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

Dan itu menjelaskan penggunaan “b < a ? a : b”bukannya “a < b ? b : a”:

Perhatikan bahwa maks () templat menurut [StepanovNotes] dengan sengaja mengembalikan “b <a? a: b "bukannya" a <b? b: a ”untuk memastikan bahwa fungsi berperilaku dengan benar meskipun kedua nilai tersebut setara tetapi tidak sama.

Bagaimana cara memahami " even if the two values are equivalent but not equal."? “a < b ? b : a”sepertinya punya hasil yang sama buat saya.

Nan Xiao
sumber
8
Looks salah kepada saya ... Kedua jawaban tersebut "benar", tetapi jika adan byang setara , maka !(a < b) && !(b < a)benar, sehingga a < bdan b < akeduanya palsu, sehingga dalam b < a ? a : b, bdikembalikan, yang tidak apa yang Anda inginkan ... Anda ingin a < b ? b : a.
Holt
1
Anda sering dapat membedakan antara ekuivalen adan bdengan std::addressofet. Al.
Caleth
14
Jika Anda melakukannya a = max(a, b);(berulang kali), Anda mungkin tidak ingin mengganti yang atidak perlu.
Bo Persson
2
BTW templat ini harus mengambil parameter dengan const-reference dan mengembalikannya dengan const-reference, jika tidak, Anda melakukan banyak salinan tidak berguna (dan Anda akan menimpanya adengan salinan a).
Holt
3
@ Caleth: Jenis kanonik yang memiliki keduanya kesetaraan dan kesetaraan adalah CaseInsensitiveString. Untuk tipe itu, baik a <A maupun A <a. Tetapi std::addressoftidak relevan. Padahal, untuk yang diberikan T max(T a, T b)kita sudah tahu addressof(a) != addressof(b).
MSalters

Jawaban:

150

std::max(a, b) memang ditentukan untuk kembali a ketika keduanya setara.

Itu dianggap kesalahan oleh Stepanov dan yang lainnya karena merusak properti berguna yang diberikan adan b, Anda selalu dapat mengatasinya {min(a, b), max(a, b)}; untuk itu, Anda ingin max(a, b)kembali bketika argumennya setara.

TC
sumber
48
Dari tautan itu "Sulit bagi saya untuk menyalahkan orang yang melakukannya: setelah semua, mereka hanya mengikuti spesifikasi standar C ++ max yang ditulis oleh saya. Butuh beberapa tahun untuk melihat bahwa saya salah." - Wow!
Jack Aidley
23
tidak bisakah kamu melakukannya {min(a, b), max(b, a)}?
Kapten Man
12
@ Kapten Man: Ya, tapi masih kurang jelas. Saya berpendapat bahwa masuk akal jika max(a,b)mengembalikan return if-and-only-if min(a,b)b, dan sebaliknya sehingga keduanya merupakan kebalikan dari yang lainnya dan set (unordered) {min(a,b), max(a,b)}selalu sama dengan {a,b}.
Jack Aidley
5
@ jpmc26: Jika seseorang misalnya mengurutkan daftar peristiwa berdasarkan waktu, orang tidak perlu peduli apakah operasi penyortiran stabil untuk peduli tentang memiliki setiap peristiwa yang muncul tepat sekali dalam input, juga muncul tepat sekali dalam output. Operasi tertentu (seperti menemukan dan menghilangkan acara yang digandakan) mungkin memerlukan penggunaan pemesanan lengkap, tetapi dalam banyak kasus lain, mungkin dapat diterima untuk membuat daftar acara simultan dalam urutan sewenang-wenang, tetapi tidak untuk menduplikasi atau menghilangkannya.
supercat
2
@supercat Menerapkan mindan maxuntuk apa pun kecuali cap waktu (tombol sortir) dalam skenario itu tidak masuk akal. Peristiwa (objek) itu sendiri seharusnya tidak dapat dibandingkan jika kesetaraan tidak menyiratkan pertukaran. Satu-satunya cara {min(a, b), max(a, b)}membuat setiap akal sebagai mekanisme semacam ini jika benda yang dipertukarkan.
jpmc26
62

Jawaban ini menjelaskan mengapa kode yang diberikan salah dari sudut pandang standar C ++, tetapi tidak sesuai konteks.

Lihat jawaban @ TC untuk penjelasan kontekstual.


Standar mendefinisikan std::max(a, b)sebagai berikut [alg.min.max] (penekanan adalah milikku):

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

Membutuhkan : Tipe T adalah LessThanComparable (Tabel 18).

Kembali : Nilai lebih besar.

Keterangan : Mengembalikan argumen pertama ketika argumennya setara.

Setara sini berarti bahwa !(a < b) && !(b < a)adalahtrue [alg.sorting # 7] .

Secara khusus, jika adan bsetara, baik a < bdan b < asedang false, maka nilai di sebelah kanan :akan dikembalikan di operator bersyarat, jadi aharus di sebelah kanan, jadi:

a < b ? b : a

... sepertinya jawaban yang benar. Ini adalah versi yang digunakan oleh libstdc ++ dan libc ++ .

Jadi informasi dalam kutipan Anda tampaknya salah menurut standar saat ini, tetapi konteks yang didefinisikan mungkin berbeda.

Suaka
sumber
4
Link Godbolt yang menjelaskan masalah ini (terima kasih @songyuanyao untuk definisi X).
Holt
1
@JackAidley Saya telah mengedit jawaban untuk menentukan bahwa alasannya menargetkan standar saat ini.
Holt
@codekaizer saya sebenarnya merujuk ke "Jika kita mendefinisikan equiv (a, b) sebagai! comp (a, b) &&! comp (b, a)" . Saya telah mengubah tautan ke kutipan yang lebih baik (3 baris di bawah dalam standar ...).
Holt
1
Terkejut tidak ada yang menyebutkan floating point, di mana a<bdan b<akeduanya bisa salah karena mereka tidak terurut (satu atau keduanya NaN, jadi ==juga salah). Itu bisa dilihat sebagai semacam kesetaraan. terkait longgar: maxsd a, bmengimplementasikan instruksi x86 a = max(b,a) = b < a ? a : b. ( Apa instruksi yang memberikan FP min tanpa cabang dan maks pada x86? ). Instruksi menjaga operan sumber (yang ke-2) pada unordered, sehingga loop di atas array akan memberi Anda NaN jika ada NaN. Tetapi max_seen = max(max_seen, a[i])akan mengabaikan NaNs.
Peter Cordes
Juga lihat Catatan Stepanov tentang Pemrograman
Shafik Yaghmour
21

Intinya adalah yang harus dikembalikan ketika mereka setara; std::maxharus kembali a(yaitu argumen pertama) untuk kasus ini.

Jika mereka setara, kembali a.

Jadi a < b ? b : aharus digunakan; di sisi lain, b < a ? a : b;akan kembali dengan btidak benar.

(Seperti yang dikatakan @Holt, kutipannya sepertinya berlawanan.)

"kedua nilai tersebut setara tetapi tidak sama" berarti mereka memiliki nilai yang sama ketika dibandingkan, tetapi mereka akan menjadi objek yang berbeda di beberapa aspek lainnya.

misalnya

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
songyuanyao
sumber
1
Bisakah Anda jelaskan mengapa std::max(a, b)harus kembali a, jika adan bsetara?
眠 り ネ ロ ク
4
@ ネ ロ ク - Ini hanya pilihan sewenang-wenang pada bagian standar . Meskipun agak bisa diperdebatkan jika itu bagus.
StoryTeller - Unslander Monica
Apakah hanya saya atau ini bertentangan dengan pertanyaan? Jika adan bsetara, maka !(a < b) && !(b < a)benar, jadi a < bdan b < asalah, jadi ...?
Holt
2
@ ネ ロ ク Saya kira standar hanya ingin membuatnya menentukan; ketika mereka setara, mana yang harus dikembalikan.
songyuanyao
1
terima kasih telah menyegarkan ingatan saya tentang mengapa suatu objek akan "setara" tetapi tidak "sama".
Jeff Walker