Apa itu komparator transparan?

106

Di C ++ 14, wadah asosiatif tampaknya telah berubah dari C ++ 11 - [asosiatif.reqmts] / 13 mengatakan:

Template fungsi anggota find, count, lower_bound, upper_bound, dan equal_rangeakan tidak berpartisipasi dalam resolusi yang berlebihan kecuali jenis Compare::is_transparentada.

Apa tujuan membuat komparator "transparan"?

C ++ 14 juga menyediakan template perpustakaan seperti ini:

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

Jadi misalnya, tidakstd::set<T, std::less<T>> akan memiliki komparator transparan, tetapi akan memilikinya.std::set<T, std::less<>>

Masalah apa yang dipecahkan ini, dan apakah ini mengubah cara kerja wadah standar? Sebagai contoh, parameter template std::setmasih Key, Compare = std::less<Key>, ..., jadi tidak default set kehilangan nya find, count, dll anggota?

Kerrek SB
sumber
Misalnya, lihat deskripsi preferensi cp ini . Dan saya merasa bodoh sekarang, karena saya mencatat kata " template fungsi anggota " ...
Kerrek SB
cppreference juga memiliki uraian singkat
Cubbi

Jawaban:

60

Masalah apa yang diselesaikan ini,

Lihat jawaban Dietmar dan jawaban remyabel .

dan apakah ini mengubah cara kerja wadah standar?

Tidak, tidak secara default.

Kelebihan template fungsi anggota baru finddll. Memungkinkan Anda menggunakan jenis yang sebanding dengan kunci penampung, alih-alih menggunakan jenis kunci itu sendiri. Lihat N3465 oleh Joaquín Mª López Muñoz untuk alasan dan detail, proposal yang ditulis dengan cermat untuk menambahkan fitur ini.

Pada pertemuan Bristol, POKJA setuju bahwa fitur pencarian heterogen berguna dan diinginkan, tetapi kami tidak dapat memastikan bahwa proposal Joaquín akan aman di semua kasus. Proposal N3465 akan menyebabkan masalah serius untuk beberapa program (lihat bagian Dampak pada kode yang ada ). Joaquín menyiapkan draf proposal yang diperbarui dengan beberapa implementasi alternatif dengan trade-off yang berbeda, yang sangat berguna membantu POKJA memahami pro dan kontra, tetapi mereka semua berisiko melanggar beberapa program dengan cara tertentu sehingga tidak ada konsensus untuk menambahkan fitur tersebut. Kami memutuskan bahwa meskipun tidak aman menambahkan fitur tanpa syarat, akan aman jika dinonaktifkan secara default dan hanya "ikut serta".

Perbedaan utama dari proposal N3657 (yang merupakan revisi menit-menit terakhir oleh saya sendiri dan STL berdasarkan N3465 dan draf yang kemudian tidak diterbitkan oleh Joaquín) adalah menambahkan is_transparentjenis sebagai protokol yang dapat digunakan untuk ikut serta ke fungsi baru.

Jika Anda tidak menggunakan "transparent functor" (yaitu yang mendefinisikan sebuah is_transparenttipe) maka kontainer akan berperilaku sama seperti yang biasa mereka lakukan, dan itu masih default.

Jika Anda memilih untuk menggunakan std::less<>(yang baru untuk C ++ 14) atau jenis "functor transparan" lainnya maka Anda mendapatkan fungsionalitas baru.

Penggunaannya std::less<>mudah dengan templat alias:

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

Namanya is_transparentberasal dari STL's N3421 yang menambahkan "operator berlian" ke C ++ 14. Sebuah "functor transparan" adalah salah satu yang menerima semua jenis argumen (yang tidak harus sama) dan hanya meneruskan argumen tersebut ke operator lain. Functor seperti itu kebetulan persis seperti yang Anda inginkan untuk pencarian heterogen dalam wadah asosiatif, sehingga jenisnya is_transparentditambahkan ke semua operator berlian dan digunakan sebagai jenis tag untuk menunjukkan fungsionalitas baru yang harus diaktifkan dalam wadah asosiatif. Secara teknis, kontainer tidak memerlukan "fungsi transparan", hanya satu yang mendukung pemanggilan dengan tipe heterogen (mis. pointer_compTipe di https://stackoverflow.com/a/18940595/981959 tidak transparan menurut definisi STL,pointer_comp::is_transparentmemungkinkannya digunakan untuk memecahkan masalah). Jika Anda hanya pernah lookup di Anda std::set<T, C>dengan tombol jenis Tatau intkemudian Chanya perlu callable dengan argumen tipe Tdan int(dalam rangka baik), tidak perlu untuk benar-benar transparan. Kami menggunakan nama itu sebagian karena kami tidak dapat menemukan nama yang lebih baik (saya lebih suka is_polymorphickarena fungsi tersebut menggunakan polimorfisme statis, tetapi sudah ada std::is_polymorphicsifat tipe yang mengacu pada polimorfisme dinamis).

Jonathan Wakely
sumber
3
Hei, apakah Anda orang yang STL berkata, "Tentu saja Anda bisa melakukan pemotongan argumen template di kepala Anda" dalam pembicaraan woolstar terkait?
Kerrek SB
10
Tidak, saya tidak ada di sana, tetapi ada orang dengan kompiler yang jauh lebih sesuai di kepala mereka daripada yang saya miliki :)
Jonathan Wakely
Saya kira "operator berlian" mengacu <>pada proposal tertaut, tetapi proposal itu tidak memperkenalkan <>- ini adalah sintaks yang ada untuk daftar parameter template kosong. "Fungsi operator berlian" akan sedikit membingungkan.
Qwertie
33

Dalam C ++ 11 tidak ada anggota template find(), lower_bound()dll Artinya, tidak ada yang hilang oleh perubahan ini. Template anggota diperkenalkan dengan n3657 untuk memungkinkan kunci heterogen digunakan dengan wadah asosiatif. Saya tidak melihat contoh konkret di mana ini berguna kecuali untuk contoh yang baik dan buruk!

The is_transparentPenggunaan ini dimaksudkan untuk menghindari konversi yang tidak diinginkan. Jika templat anggota tidak dibatasi, kode yang ada dapat melewati objek secara langsung yang akan dikonversi tanpa templat anggota. Contoh use-case dari n3657 adalah mencari sebuah objek dalam std::set<std::string>menggunakan string literal: dengan definisi C ++ 11, sebuah std::stringobjek dibangun saat meneruskan literal string ke fungsi anggota yang sesuai. Dengan perubahan itu dimungkinkan untuk menggunakan string literal secara langsung. Jika objek fungsi perbandingan yang mendasari diimplementasikan secara eksklusif dalam hal std::stringitu adalah buruk karena sekarang a std::stringakan dibuat untuk setiap perbandingan. Di sisi lain, jika objek fungsi perbandingan yang mendasari dapat mengambilstd::string dan string literal, yang mungkin menghindari konstruksi objek sementara.

is_transparentTipe bertingkat dalam objek fungsi perbandingan menyediakan cara untuk menentukan apakah fungsi anggota templated harus digunakan: jika objek fungsi perbandingan dapat menangani argumen heterogen, ia mendefinisikan tipe ini untuk menunjukkan bahwa ia dapat menangani argumen yang berbeda secara efisien. Misalnya, objek fungsi operator baru hanya mendelegasikan operator<()dan mengklaim transparan. Itu, setidaknya, bekerja std::stringyang kelebihan beban lebih sedikit daripada yang char const*dianggap oleh operator sebagai argumen. Karena objek fungsi ini juga baru, meskipun mereka melakukan hal yang salah (yaitu memerlukan konversi untuk beberapa jenis), setidaknya, itu bukan perubahan diam yang mengakibatkan penurunan performa.

Dietmar Kühl
sumber
Terima kasih - lihat komentar saya untuk pertanyaan lain: Apakah Anda mendapatkan perilaku transparan secara default?
Kerrek SB
8
@KerrekSB: perilaku transparan diaktifkan ketika is_transparentdidefinisikan dalam objek fungsi perbandingan menurut 23.2.4 [asosiatif.reqmts] paragraf 13. Objek fungsi perbandingan default adalah std::less<Key>sesuai dengan 23.4.2 [asosiatif.map.syn] dan 23.4. 3 [asosiatif.set.syn]. Menurut 20.10.5 [perbandingan] ayat 4 template umum untuk std::less<...>tidak tidak menentukan jenis bersarang is_transparenttetapi std::less<void>spesialisasi tidak. Artinya, tidak, Anda tidak mendapatkan operator transparan secara default.
Dietmar Kühl
Apakah Anda punya ide untuk penamaan? Maksud saya kenapa is_transparent?
plasmacel
Anda ingin "contoh konkret di mana ini berguna"? Ini kasus penggunaan saya
spraff
19

Berikut ini adalah semua copy-pasta dari n3657 .

T. Apa tujuan membuat komparator "transparan"?

A. Fungsi pencarian container asosiatif (find, lower_bound, upper_bound, equal_range) hanya mengambil argumen key_type, yang mengharuskan pengguna untuk membangun (baik secara implisit maupun eksplisit) objek dari key_type untuk melakukan pencarian. Ini mungkin mahal, misalnya membuat objek besar untuk dicari dalam satu set ketika fungsi pembanding hanya melihat satu bidang objek. Ada keinginan kuat di antara pengguna untuk dapat melakukan pencarian menggunakan tipe lain yang sebanding dengan key_type tersebut.

Q. Masalah apa yang dipecahkan ini

A. POKJA memiliki kekhawatiran tentang kode seperti berikut ini:

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

Dalam C ++ 11 ini akan membuat satu std :: string sementara dan kemudian membandingkannya dengan elemen untuk menemukan kuncinya.

Dengan perubahan yang diusulkan oleh N3465, fungsi std :: set :: find () akan menjadi template yang tidak dibatasi yang akan meneruskan char * const melalui ke fungsi pembanding, std :: less, yang akan membuat std :: string sementara untuk setiap perbandingan. POKJA menganggap masalah kinerja ini sebagai masalah serius. Fungsi template find () juga akan mencegah menemukan NULL dalam wadah pointer, yang menyebabkan kode yang sebelumnya valid tidak lagi dikompilasi, tetapi ini dianggap sebagai masalah yang kurang serius daripada regresi performa diam

P. Apakah ini mengubah cara kerja wadah standar

A. Proposal ini mengubah wadah asosiatif dalam dan dengan membebani fungsi anggota pencarian dengan templat fungsi anggota. Tidak ada perubahan bahasa.

Q. begitu juga set default kehilangan anggota yang ditemukan, dihitung, dll

A. Hampir semua kode C ++ 11 yang ada tidak terpengaruh karena fungsi anggota tidak ada kecuali fitur pustaka C ++ 14 baru digunakan sebagai fungsi perbandingan.

Mengutip Yakk ,

Di C ++ 14, std :: set :: find adalah fungsi template jika Compare :: is_transparent ada. Jenis yang Anda berikan tidak harus Key, hanya setara di bawah pembanding Anda.

dan n3657,

Menambahkan ayat 13 di 23.2.4 [associative.reqmts]: Fungsi anggota template menemukan, lower_bound, upper_bound dan equal_range tidak akan berpartisipasi dalam resolusi yang berlebihan kecuali tipe Bandingkan :: is_transparent tidak ada tidak eksis.

n3421 memberikan contoh "Fungsi Operator Transparan" .

The kode lengkap di sini .

Komunitas
sumber
1
Apakah std::set<std::string>benar-benar mendapat manfaat dari "passing the char const *through", atau apakah Anda perlu membuatnya std::set<std::string, std::less<>>?
Kerrek SB
@Kerrek Saya pikir "melewati char const *" adalah masalah yang mereka coba hindari, jika saya tidak salah. Lihatlah kata-katanya: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.
Kutipan Anda dan saya pada paragraf 13 mengatakan sebaliknya: "kecuali jenisnya ada / tidak ada" ...?!
Kerrek SB
4
@KerrekSB, itu salah saya, N3657 seharusnya mengatakan "ada" tetapi saya menulis "tidak ada" ... itu adalah makalah yang terlambat ditulis pada menit terakhir. Standar draf sudah benar.
Jonathan Wakely
3
Ya, mungkin lebih jelas untuk mengutip apa yang saya maksud bukan apa yang sebenarnya saya katakan saat itu :)
Jonathan Wakely
7

Stephan T Lavavej berbicara tentang masalah di mana compiler terus membuat temporaries, dan bagaimana proposalnya tentang fungsi operator transparan akan menyelesaikannya di c ++ 1y

GoingNative 2013 - Dont help the Compiler (sekitar tanda jam)

woolstar
sumber