Mengapa std::set tidak memiliki "berisi" fungsi anggota?

I'm berat menggunakan std::set<int> dan sering aku hanya perlu memeriksa jika set tersebut berisi nomor atau tidak.

I'd menemukannya alami menulis:

if (myset.contains(number))
   ...

Tetapi karena kurangnya berisi anggota, saya perlu menulis rumit:

if (myset.find(number) != myset.end())
  ..

atau tidak jelas:

if (myset.count(element) > 0) 
  ..

Apakah ada alasan untuk keputusan desain ini ?

Mengomentari pertanyaan (11)
Larutan

Saya pikir itu mungkin karena mereka mencoba untuk membuat std::set dan std::multiset semirip mungkin. (Dan jelas menghitung telah sempurna makna yang masuk akal untuk std::multiset.)

Secara pribadi saya pikir ini adalah sebuah kesalahan.

Itu doesn't terlihat begitu buruk jika anda berpura-pura bahwa menghitung hanya kesalahan ejaan berisi dan menulis tes seperti:

if (myset.count(element)) 
   ...

It's masih malu sekalipun.

Komentar (18)

Untuk dapat menulis jika (s.berisi()), mengandung() telah kembali bool (atau jenis convertible untuk bool, yang adalah cerita lain), seperti binary_search tidak.

The alasan mendasar di balik keputusan desain tidak untuk melakukannya dengan cara ini adalah yang berisi() yang kembali bool akan kehilangan informasi berharga tentang di mana elemen dalam koleksi ini. find() mempertahankan dan mengembalikan informasi tersebut dalam bentuk sebuah iterator, karena itu adalah pilihan yang lebih baik untuk generik perpustakaan seperti STL. Ini selalu menjadi prinsip untuk Alex Stepanov, karena ia telah sering dijelaskan (misalnya, [di sini][1]).

Untuk menghitung () pendekatan secara umum, meskipun itu's sering oke pemecahan masalah, masalah dengan itu adalah bahwa ini tidak bekerja lebih dari berisi() akan dilakukan.

Itu bukan untuk mengatakan bahwa bool berisi() isn't yang sangat bagus untuk dimiliki atau bahkan diperlukan. Beberapa saat yang lalu kami memiliki diskusi panjang tentang hal ini masalah yang sama di ISO Standar C++ - masa Depan Proposal kelompok.

Komentar (5)

Kekurangan itu karena tidak ada yang ditambahkan. Tak ada yang menambahkan ini karena wadah dari STL bahwa std perpustakaan yang tergabung di mana dirancang untuk menjadi minimal di antarmuka. (Perhatikan bahwa std::string tidak datang dari STL dalam cara yang sama).

Jika anda don't pikiran beberapa sintaks aneh, anda dapat palsu itu:

template
struct contains_t {
  K&& k;
  template
  friend bool operator->*( C&& c, contains_t&& ) {
    auto range = std::forward(c).equal_range(std::forward(k));
    return range.first != range.second;
    // faster than:
    // return std::forward(c).count( std::forward(k) ) != 0;
    // for multi-meows with lots of duplicates
  }
};
template
containts_t contains( K&& k ) {
  return {std::forward(k)};
}

gunakan:

if (some_set->*contains(some_element)) {
}

Pada dasarnya, anda dapat menulis metode ekstensi untuk sebagian besar C++ std jenis menggunakan teknik ini.

Hal ini membuat banyak lebih masuk akal untuk hanya melakukan ini:

if (some_set.count(some_element)) {
}

tapi saya geli dengan metode penyuluhan metode.

Benar-benar menyedihkan adalah bahwa tulisan yang efisien berisi bisa menjadi lebih cepat pada multimap atau multiset, karena mereka hanya harus menemukan satu elemen, sementara hitung untuk menemukan masing-masing dari mereka dan menghitung mereka.

Suatu multiset yang mengandung 1 miliar salinan dari 7 (anda tahu, dalam kasus anda kehabisan) dapat benar-benar lambat .count(7), tapi bisa sangat cepat berisi(7).

Dengan penjelasan di atas metode penyuluhan, kita bisa membuatnya lebih cepat untuk kasus ini dengan menggunakan lower_bound, membandingkan end, dan kemudian membandingkan elemen. Melakukan hal itu untuk unordered meow serta memerintahkan meow akan memerlukan mewah SFINAE atau wadah tertentu overload namun.

Komentar (5)

Anda melihat ke dalam kasus tertentu dan tidak melihat gambaran yang lebih besar. Seperti yang dinyatakan dalam dokumentasi std::set memenuhi kebutuhan AssociativeContainer konsep. Untuk itu konsep ini tidak masuk akal untuk memiliki berisi metode, seperti yang cukup banyak berguna untuk std::multiset dan std::multimap, tapi menghitung bekerja dengan baik untuk mereka semua. Meskipun metode berisi bisa ditambahkan sebagai alias untuk menghitung untuk std::set, std::map dan mereka hash versi (seperti panjang untuk ukuran () dalam std::string ), tapi tampak seperti perpustakaan pencipta tidak melihat kebutuhan nyata untuk itu.

Komentar (8)

Meskipun aku don't tahu mengapa std::set tidak berisi tapi count yang hanya pernah kembali 0 atau 1, anda dapat menulis kerangka berisi penolong fungsi seperti ini:

template
auto contains(const Container& v, const T& x)
-> decltype(v.find(x) != v.end())
{
    return v.find(x) != v.end();
}

Dan menggunakannya seperti ini:

    if (contains(myset, element)) ...
Komentar (10)

Alasan yang benar untuk mengatur adalah sebuah misteri bagi saya, tapi salah satu penjelasan yang mungkin untuk ini desain yang sama di peta bisa untuk mencegah orang-orang untuk menulis kode yang tidak efisien oleh kecelakaan:

if (myMap.contains("Meaning of universe"))
{
    myMap["Meaning of universe"] = 42;
}

Yang akan menghasilkan dua peta lookup.

Sebaliknya, anda dipaksa untuk mendapatkan sebuah iterator. Ini memberi anda sebuah mental petunjuk bahwa anda harus menggunakan kembali iterator:

auto position = myMap.find("Meaning of universe");
if (position != myMap.cend())
{
    position->second = 42;
}

yang hanya mengkonsumsi satu peta lookup.

Ketika kita menyadari bahwa set dan peta yang dibuat dari daging yang sama, kita dapat menerapkan prinsip ini juga untuk mengatur. Artinya, jika kita ingin bertindak pada item dalam set hanya jika hadir di set, desain ini dapat mencegah kita dari menulis kode seperti ini:

struct Dog
{
    std::string name;
    void bark();
}

operator bark();
}

Tentu saja semua ini adalah spekulasi belaka.

Komentar (4)

Bagaimana binary_search ?

 set  set1;
 set1.insert(10);
 set1.insert(40);
 set1.insert(30);
 if(std::binary_search(set1.begin(),set1.end(),30))
     bool found=true;
Komentar (2)

Alasan lain adalah bahwa hal itu akan memberikan programmer kesan palsu bahwa std::set adalah satu set dalam matematika teori akal. Jika mereka melaksanakan itu, maka banyak pertanyaan lain akan mengikuti: jika std::set telah mengandung() untuk nilai, mengapa doesn't memilikinya untuk satu set? Di mana uni(), persimpangan() dan lain mengatur operasi dan predikat?

Jawabannya adalah, tentu saja, bahwa beberapa set operasi yang sudah diimplementasikan sebagai fungsi in (std::set_union() dll.) dan lain-lain adalah sebagai mudah dilaksanakan karena mengandung(). Fungsi dan fungsi benda-benda bekerja lebih baik dengan matematika abstraksi dari objek anggota, dan mereka tidak terbatas untuk tertentu jenis wadah.

Jika salah satu kebutuhan untuk melaksanakan penuh matematika-set fungsi, dia tidak hanya pilihan yang mendasari wadah, tetapi juga ia memiliki pilihan rincian pelaksanaan, misalnya, akan theory_union() fungsi bekerja dengan benda berubah, lebih cocok untuk pemrograman fungsional, atau akan memodifikasi operan dan menyimpan memori? Itu akan diimplementasikan sebagai fungsi objek dari awal atau'a menjadi lebih baik dengan menerapkan C-fungsi, dan menggunakan std::fungsi<> jika diperlukan?

Seperti sekarang, std::set adalah hanya sebuah wadah, cocok untuk pelaksanaan diatur dalam matematika akal, tetapi hal ini hampir seperti jauh dari yang teoritis ditetapkan sebagai std::vektor dari yang teoritis vektor.

Komentar (0)