Tại sao `std :: string :: find ()` không trả về trình lặp kết thúc khi gặp lỗi?


29

Tôi thấy hành vi của std::string::findnó không phù hợp với các thùng chứa C ++ tiêu chuẩn.

Ví dụ

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

Nhưng đối với một chuỗi,

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

Tại sao không nên myStr.find('!')trả lại thất bại myStr.end()thay vì std::string::npos?

std::stringnó có phần đặc biệt khi so sánh với các container khác, tôi tự hỏi liệu có một số lý do thực sự đằng sau điều này. (Đáng ngạc nhiên, tôi không thể tìm thấy bất cứ ai đặt câu hỏi này ở bất cứ đâu).


5
Tôi nghĩ chỉ có câu trả lời hợp lý là gần với câu trả lời cho câu hỏi: 'Tại sao bánh hotdog được đóng gói trong 4 và bánh hotdog trong 6?' Chà, đó là cách thế giới hạnh phúc trở thành
bartop 17/10/19

Kiểm tra cái này
NutCracker

IMHO, một lý do cho hành vi này sẽ là std::stringbên trong bao gồm các nhân vật là các yếu tố rẻ tiền (liên quan đến bộ nhớ). Và, hơn nữa, nhân vật là loại duy nhất std::stringcó thể chứa. Mặt khác, std::mapbao gồm các yếu tố phức tạp hơn. Ngoài ra, đặc điểm kỹ thuật của việc std::map::findnói rằng nó được cho là tìm thấy một yếu tố và đặc điểm kỹ thuật std::string::findnói rằng nhiệm vụ của nó là tìm vị trí.
NutCracker

Đối với bản đồ, bạn không thể có trình lặp npose để trình lặp kết thúc được sử dụng. Đối với chuỗi, chúng ta có thể sử dụng npose, vậy tại sao không :)
LF

Câu trả lời:


28

Để bắt đầu, std::stringgiao diện nổi tiếng là cồng kềnh và không nhất quán, xem Gotw84 của Herb Sutter về chủ đề này. Nhưng tuy nhiên, có một lý do đằng sau việc std::string::findtrả lại một chỉ mục : std::string::substr. Hàm thành viên tiện lợi này hoạt động trên các chỉ số, vd

const std::string src = "abcdefghijk";

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

Bạn có thể triển khai substrsao cho nó chấp nhận các trình vòng lặp vào chuỗi, nhưng sau đó chúng ta sẽ không cần phải chờ đợi lâu để khiếu nại lớn mà std::stringkhông sử dụng được và phản trực giác. Vì vậy, khi std::string::substrchấp nhận các chỉ số, làm thế nào bạn sẽ tìm thấy chỉ mục của lần xuất hiện đầu tiên 'd'trong chuỗi đầu vào ở trên để in ra mọi thứ bắt đầu từ chuỗi con này?

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

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

Đây cũng có thể không phải là những gì bạn muốn. Do đó, chúng ta có thể std::string::findtrả về một chỉ mục và ở đây chúng ta:

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

Nếu bạn muốn làm việc với các trình vòng lặp, hãy sử dụng <algorithm>. Họ cho phép bạn ở trên như

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

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

4
Điểm tốt. Tuy nhiên, thay vì trả lại một iterator, std::string::findvẫn có thể trả về size(), thay vì nposgiữ lại khả năng tương thích với substr, đồng thời tránh một số dấu hiệu phụ.
erenon

1
@erenon Có thể, nhưng std::string::substrđã bao gồm trường hợp "bắt đầu ở đây cho đến khi kết thúc" với một tham số mặc định cho chỉ mục thứ hai ( npos). Tôi đoán trở về size()cũng sẽ gây nhầm lẫn và có một trọng điểm theo nghĩa đen như thế nposcó thể là lựa chọn tốt hơn?!
Lubgr

@lubgr Nhưng nếu std::string::findtrả về một iterator, std::string::substrcó lẽ cũng sẽ chấp nhận một iterator cho vị trí bắt đầu. Ví dụ của bạn với find sẽ giống nhau trong cả hai trường hợp trong thế giới thay thế này.
Mattias Wallin

@MattiasWallin Điểm tốt. Nhưng std::string::substrvới một đối số iterator mở ra cơ hội cho một trường hợp UB nữa (bên cạnh kịch bản kết thúc có thể xảy ra tốt như nhau với các chỉ số hoặc các trình lặp): truyền một trình lặp lặp đến một chuỗi khác.
Lubgr

3

Điều này là do std::stringcó hai giao diện:

  • Giao diện dựa trên trình vòng lặp chung được tìm thấy trên tất cả các container
  • Các std::stringcụ chỉ số dựa trên giao diện

std::string::findlà một phần của giao diện dựa trên chỉ mục và do đó trả về các chỉ mục.

Sử dụng std::findđể sử dụng giao diện dựa trên iterator chung.

Sử dụng std::vector<char>nếu bạn không muốn giao diện dựa trên chỉ mục (không làm điều này).

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.