`Std :: string :: find ()` neden hatalarda son yineleyiciyi döndürmüyor?


29

Davranışını std::string::findstandart C ++ kaplarıyla tutarsız buluyorum .

Örneğin

std::map<int, int> myMap = {{1, 2}};
auto it = myMap.find(10);  // it == myMap.end()

Ama bir ip için,

std::string myStr = "hello";
auto it = myStr.find('!');  // it == std::string::npos

Başarısız myStr.find('!')iade neden myStr.end()yerine neden olmasın std::string::npos?

Yana std::stringbaşka kaplarla karşılaştırıldığında biraz özeldir, bu arkasında bazı gerçek neden olup olmadığını merak ediyorum. (Şaşırtıcı bir şekilde, bunu hiçbir yerde sorgulayan kimse bulamadım).


5
Bence sadece makul bir cevap şu soruya cevap vermeye yakın: 'Neden sosisli sandviçler 4'lü ve sosisli çörekler 6'lı?' Dünya böyle oldu
bartop


IMHO, bu davranışın bir nedeni, std::stringdahili olarak (bellek açısından) ucuz öğeler olan karakterlerden oluşmasıdır. Ve ayrıca, karakter std::stringiçerebilen tek türdür . Öte yandan, std::mapdaha karmaşık unsurlardan oluşur. Ayrıca, spesifikasyonunun std::map::findbir eleman bulması gerektiğini std::string::findsöyler ve spesifikasyonunun görevi pozisyon bulmak olduğunu söyler.
NutCracker

Harita için, bir npos yineleyiciniz olamaz, böylece son yineleyici kullanılır. Dize için npos kullanabiliriz, neden olmasın :)
LF

Yanıtlar:


28

Başlangıç ​​olarak, std::stringarayüzün şişirildiği ve tutarsız olduğu iyi bilinir , bu konuda Herb Sutter'in Gotw84 sayfasına bakın . Ama yine de, arkasında bir mantık var std::string::findbir dizin dönen: std::string::substr. Bu kolaylık üyesi işlevi endekslerde çalışır, ör.

const std::string src = "abcdefghijk";

std::cout << src.substr(2, 5) << "\n";

substrDizeyi yineleyicileri kabul edecek şekilde uygulayabilirsiniz , ancak daha sonra std::stringkullanılamaz ve mantıksız şikayetler için uzun süre beklememiz gerekmez . std::string::substrEndeksleri kabul ettiğinden 'd', bu alt dizeden başlayarak her şeyi yazdırmak için yukarıdaki giriş dizesindeki ilk oluşumun dizinini nasıl bulabilirdiniz ?

const auto it = src.find('d'); // imagine this returns an iterator

std::cout << src.substr(std::distance(src.cbegin(), it));

Bu da istediğiniz şey olmayabilir. Bu nedenle std::string::find, bir dizin döndürebiliriz ve işte buradayız:

const std::string extracted = src.substr(src.find('d'));

Yineleyicilerle çalışmak istiyorsanız kullanın <algorithm>. Size yukarıdaki gibi izin verir

auto it = std::find(src.cbegin(), src.cend(), 'd');

std::copy(it, src.cend(), std::ostream_iterator<char>(std::cout));

4
İyi bir nokta. Bununla birlikte, bir yineleyici std::string::finddöndürmek size()yerine, birkaç ekstra branştan kaçınarak, aynı zamanda nposuyumluluğu korumak yerine geri dönebilir substr.
erenon

1
@ erenon Belki, ama std::string::substrzaten ikinci dizin ( npos) için bir varsayılan parametre ile "burada sonuna kadar başla" durumda kapsar . Dönen size()de kafa karıştırıcı olurdu ve gerçek bir sentinel gibi nposdaha iyi bir seçim olabilir sanırım ?!
lubgr

@lubgr Ancak std::string::findyineleyici döndürürse, std::string::substrmuhtemelen başlangıç ​​konumu için yineleyici de kabul eder. Find ile örneğiniz bu alternatif dünyada her iki durumda da aynı görünecektir.
Mattias Wallin

@MattiasWallin İyi bir nokta. Ancak std::string::substrbir yineleyici argümanı ile bir başka UB vakası için kapıyı açar (endeksler veya yineleyicilerle aynı derecede iyi olabilen geçmiş senaryo dışında): başka bir dizeye başvuran bir yineleyiciyi iletme.
lubgr

3

Bunun nedeni std::stringiki arayüze sahip olmasıdır:

  • Tüm kapsayıcılarda bulunan genel yineleyici tabanlı arabirim
  • std::stringBelirli endeksi tabanlı arayüz

std::string::finddizin tabanlı arabirimin bir parçasıdır ve bu nedenle dizinleri döndürür.

std::findGenel yineleyici tabanlı arabirimi kullanmak için kullanın .

std::vector<char>Dizin tabanlı arayüzü istemiyorsanız kullanın (bunu yapmayın).

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.