Là `string.assign (string.data (), 5)` được xác định rõ hay UB?


11

Một đồng nghiệp muốn viết điều này:

std::string_view strip_whitespace(std::string_view sv);

std::string line = "hello  ";
line = strip_whitespace(line);

Tôi cho rằng việc trở về string_viewkhiến tôi không thoải mái một tiên nghiệm , và hơn nữa, các răng cưa ở đây trông giống như UB đối với tôi.

Tôi có thể nói chắc chắn rằng line = strip_whitespace(line)trong trường hợp này là tương đương với line = std::string_view(line.data(), 5). Tôi tin rằng sẽ gọi string::operator=(const T&) [with T=string_view], được xác định là tương đương line.assign(const T&) [with T=string_view], được xác định là tương đương với line.assign(line.data(), 5), được xác định để làm điều này:

Preconditions: [s, s + n) is a valid range.
Effects: Replaces the string controlled by *this with a copy of the range [s, s + n).
Returns: *this.

Nhưng điều này không nói điều gì xảy ra khi có răng cưa.

Tôi đã hỏi câu hỏi này trên cpplang Slack ngày hôm qua và nhận được câu trả lời hỗn hợp. Tìm kiếm câu trả lời siêu có thẩm quyền ở đây và / hoặc phân tích thực nghiệm về việc triển khai của các nhà cung cấp thư viện thực sự.


Tôi đã viết trường hợp thử nghiệm cho string::assign, vector::assign, deque::assign, list::assign, và forward_list::assign.

  • Libc ++ làm cho tất cả các trường hợp thử nghiệm này hoạt động.
  • Libstdc ++ làm cho tất cả chúng hoạt động ngoại trừ forward_list, đó là segfaults.
  • Tôi không biết về thư viện của MSVC.

Segfault trong libstdc ++ cho tôi hy vọng rằng đây là UB; nhưng tôi cũng thấy cả libc ++ và libstdc ++ sẽ nỗ lực rất nhiều để làm cho công việc này ít nhất là trong các trường hợp phổ biến.


Bạn đã biên dịch các trường hợp thử nghiệm với ASan và / hoặc chạy chúng theo Valgrind chưa? Điều đó sẽ loại bỏ phỏng đoán xem liệu mã có gây ra vi phạm truy cập hay không, mặc dù nó vẫn có thể hoạt động trong thực tế chứ không phải theo định nghĩa.
Konrad Rudolph

1
"Nếu bất kỳ hàm thành viên hoặc toán tử nào của basic_ chuỗi ném một ngoại lệ, thì hàm hoặc toán tử đó không có tác dụng nào khác đối với basic_ chuỗi đối tượng." - điều này buộc phân bổ dung lượng lưu trữ xảy ra trước khi lưu trữ hiện tại được giải phóng, do đó, một ngoại lệ sẽ bị ném nếu phân bổ thất bại, mà không thay đổi *this. Nhưng tôi thấy không có gì để ngăn chặn việc lưu trữ hiện tại được sử dụng lại, trong trường hợp này, điều này trở nên không xác định, vì ngữ nghĩa của việc sao chép lưu trữ là không xác định.
Sam Varshavchik


2
Đối với các thùng chứa trình tự được đề cập, chắc chắn là UB, vì vi phạm điều kiện tiên quyết của các assignyêu cầu trong [tab: container.seq.req] .
quả óc chó

Câu trả lời:


8

Chặn một vài trường hợp ngoại lệ mà bạn không phải là ngoại lệ, gọi hàm không phải là thành viên (tức là assign) trên chuỗi không hợp lệ [...] con trỏ [...] với các phần tử của nó. Điều này vi phạm điều kiện tiên quyết trên assignđó [s, s + n)là một phạm vi hợp lệ, vì vậy đây là hành vi không xác định.

Lưu ý rằng string::operator=(string const&)có ngôn ngữ cụ thể để tự gán cho bản thân.


1
Vì vậy, điểm chính xác là gì và điểm mà điều kiện tiên quyết được yêu cầu để giữ? Câu trả lời dường như giả định rằng điều kiện tiên quyết phải được giữ sau khi hàm thành viên được gọi.
quả óc chó

1
@walnut Tôi không phải là luật sư ngôn ngữ (không phải là người có kiến ​​thức về C ++ đặc biệt mở rộng), nhưng khi chúng tôi đảo ngược kịch bản của bạn, chúng tôi có thể đặt câu hỏi - phạm vi có thể bị vô hiệu trong quá trình thực thi assignkhông? Nếu có, thì chúng ta sẽ phải đặt một điểm cụ thể bên trong việc triển khai gán để đánh dấu khi chính xác có thể xảy ra sự vô hiệu và tôi tin rằng đó không phải là điều C ++ sẽ làm. Tôi có thể sai mặc dù.
Fureeish

2
@Fureeish Tôi cũng không biết, nhưng xem ví dụ về vấn đề LWG 526 , đóng là " không phải là khiếm khuyết ", đề cập đến khuyến nghị về việc đóng cửa std::vector::insert(iterator pos, const T& value)phải hoạt động nếu valuelà vào chính vectơ, vì tiêu chuẩn không xác định rằng nó được phép không hoạt động, mặc dù tham chiếu đó có thể bị vô hiệu bởi cuộc gọi.
quả óc chó

1
@walnut " bắt buộc phải hoạt động vì tiêu chuẩn không cho phép nó không hoạt động. " - thích nó . Sooo ... có đáng để hỏi những gì xảy ra trong thực tế không? Là việc thực hiện cần thiết để tạo một bản sao của đối số trong tình huống như vậy? Làm thế nào bạn thực sự có thể thực hiện nó ..? Tôi đã nghe nói về trình biên dịch yêu cầu tiêu chuẩn để làm điều không thể - đó có phải là một trong những trường hợp đó không? Bất kể, cảm ơn bạn đã bình luận!
Fureeish

1
@Fureeish Trên thực tế, ví dụ trước đây (hiện đã bị xóa) của tôi không thực sự kiểm tra những gì tôi muốn kiểm tra. Dưới đây là một ví dụ cố định cho thấy cả libc ++ và libstdc ++ thực sự sao chép trước khi chuyển sang phân bổ lại theo yêu cầu.
quả óc chó
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.