Tính hợp pháp của việc triển khai COW std :: string trong C ++ 11


117

Tôi hiểu rằng copy-on-write không phải là một cách khả thi để thực hiện tuân thủ std::stringtrong C ++ 11, nhưng khi nó được đưa ra thảo luận gần đây, tôi thấy mình không thể hỗ trợ trực tiếp câu lệnh đó.

Tôi có đúng là C ++ 11 không thừa nhận việc triển khai dựa trên COW của std::stringkhông?

Nếu vậy, hạn chế này có được nêu rõ ràng ở đâu đó trong tiêu chuẩn mới (ở đâu) không?

Hay là hạn chế này được ngụ ý, theo nghĩa nó là tác động tổng hợp của các yêu cầu mới đối std::stringvới việc ngăn cản việc thực hiện dựa trên COW std::string. Trong trường hợp này, tôi muốn quan tâm đến một chương và kiểu câu dẫn xuất của 'C ++ 11 cấm một cách hiệu quả việc std::stringtriển khai dựa trên COW '.


5
Lỗi GCC đối với chuỗi COW của họ là gcc.gnu.org/bugzilla/show_bug.cgi?id=21334#c45 . Một trong những lỗi theo dõi triển khai tương thích C ++ 11 mới của std :: string trong libstdc ++ là gcc.gnu.org/bugzilla/show_bug.cgi?id=53221
user7610

Câu trả lời:


120

Nó không được phép, vì theo tiêu chuẩn 21.4.1 p6, việc làm mất hiệu lực của trình vòng lặp / tham chiếu chỉ được phép đối với

- làm đối số cho bất kỳ hàm thư viện chuẩn nào lấy tham chiếu đến non-const basic_string làm đối số.

- Gọi các hàm thành viên không phải const, ngoại trừ toán tử [], at, front, back, begin, rbegin, end và rend.

Đối với chuỗi COW, việc gọi không phải const operator[]sẽ yêu cầu tạo một bản sao (và làm mất hiệu lực tham chiếu), điều này không được phép trong đoạn trên. Do đó, không còn hợp pháp khi có chuỗi COW trong C ++ 11.


4
Một số lý do: N2534
MM

8
-1 Logic không giữ nước. Tại thời điểm sao chép COW, không có tham chiếu hoặc trình lặp nào có thể bị vô hiệu, toàn bộ điểm của việc sao chép là các tham chiếu hoặc trình vòng lặp đó hiện đang được lấy, vì vậy việc sao chép là cần thiết. Nhưng vẫn có thể C ++ 11 không cho phép triển khai COW.
Chúc mừng và hth. - Alf

11
@ Cheersandhth.-Alf: Có thể thấy logic như sau nếu COW được cho phép: std::string a("something"); char& c1 = a[0]; std::string b(a); char& c2 = a[1]; c1 là tham chiếu đến a. Sau đó bạn "sao chép" a. Sau đó, khi bạn cố gắng lấy tham chiếu lần thứ hai, nó phải tạo một bản sao để nhận tham chiếu không phải const vì có hai chuỗi trỏ đến cùng một bộ đệm. Điều này sẽ phải làm mất hiệu lực của tham chiếu đầu tiên được lấy và chống lại phần được trích dẫn ở trên.
Dave S

9
@ Cheersandhth.-Alf, theo này , thực hiện COW ít nhất của GCC không thực hiện chính xác những gì Daves nói. Vì vậy, ít nhất kiểu BÒ đó bị cấm theo tiêu chuẩn.
Tavian Barnes

4
@Alf: Câu trả lời này lập luận rằng không phải const operator[](1) phải tạo một bản sao và (2) làm như vậy là bất hợp pháp. Bạn không đồng ý với điểm nào trong hai điểm đó? Nhìn vào nhận xét đầu tiên của bạn, có vẻ như một triển khai có thể chia sẻ chuỗi, ít nhất là theo yêu cầu này, cho đến khi nó được truy cập, nhưng cả quyền truy cập đọc và ghi sẽ cần phải hủy chia sẻ nó. Đó là lý do của bạn?
Ben Voigt

48

Câu trả lời của Dave Sgbjbaanbđúng . (Và Luc Danton cũng đúng, mặc dù đó là tác dụng phụ của việc cấm chuỗi BÒ hơn là quy tắc ban đầu cấm nó.)

Nhưng để làm sáng tỏ một số nhầm lẫn, tôi sẽ thêm một số giải thích thêm. Các nhận xét khác nhau liên kết đến nhận xét của tôi trên bugzilla GCC , ví dụ sau:

std::string s("str");
const char* p = s.data();
{
    std::string s2(s);
    (void) s[0];
}
std::cout << *p << '\n';  // p is dangling

Điểm của ví dụ đó là để chứng minh tại sao chuỗi tham chiếu được tính (COW) của GCC không hợp lệ trong C ++ 11. Tiêu chuẩn C ++ 11 yêu cầu mã này hoạt động chính xác. Không có gì trong mã cho phépp vô hiệu trong C ++ 11.

Sử dụng std::stringtriển khai tính tham chiếu cũ của GCC , mã đó có hành vi không xác định, vì p bị vô hiệu hóa, trở thành một con trỏ lơ lửng. (Điều xảy ra là khi s2được xây dựng, nó chia sẻ dữ liệu với s, nhưng việc lấy tham chiếu không phải const thông qua s[0]yêu cầu dữ liệu không được chia sẻ, s"bản sao khi ghi" cũng vậy vì tham chiếu s[0]có thể được sử dụng để ghi vào s, sau đó s2đi ra khỏi phạm vi, phá hủy mảng được trỏ tới p).

Tiêu chuẩn C ++ 03 cho phép rõ ràng hành vi đó trong 21.3 [lib.basic.string] p5, nơi nó nói rằng sau một cuộc gọi đến data()cuộc gọi đầu tiênoperator[]() có thể làm mất hiệu lực các con trỏ, tham chiếu và trình vòng lặp. Vì vậy, chuỗi COW của GCC là một triển khai C ++ 03 hợp lệ.

Tiêu chuẩn C ++ 11 không còn cho phép hành vi đó nữa, vì không có lệnh gọi tới nào operator[]()có thể làm mất hiệu lực các con trỏ, tham chiếu hoặc trình vòng lặp, bất kể chúng có theo lệnh gọi tới hay không data().

Vì vậy, ví dụ trên phải hoạt động trong C ++ 11, nhưng không hoạt động với loại chuỗi COW của libstdc ++, do đó loại chuỗi COW đó không được phép trong C ++ 11.


3
Việc triển khai không chia sẻ trên lệnh gọi tới .data()(và trên mọi lượt trả về con trỏ, tham chiếu hoặc trình lặp) không bị vấn đề đó. Tức là (bất biến) một bộ đệm bất kỳ lúc nào không được chia sẻ, hoặc không được chia sẻ với không có giới thiệu bên ngoài. Tôi nghĩ rằng bạn đã dự định nhận xét về ví dụ này như một báo cáo lỗi không chính thức-dưới dạng-một-nhận xét, rất xin lỗi vì đã hiểu nhầm nó! Nhưng như bạn có thể thấy bằng cách xem xét việc triển khai như tôi mô tả ở đây, hoạt động tốt trong C ++ 11 khi noexceptcác yêu cầu bị bỏ qua, ví dụ không nói bất kỳ điều gì về hình thức. Tôi có thể cung cấp mã nếu bạn muốn.
Chúc mừng và hth. - Alf

7
Nếu bạn hủy chia sẻ trên hầu hết mọi quyền truy cập vào chuỗi thì bạn sẽ mất tất cả lợi ích của việc chia sẻ. Việc triển khai COW phải thực tế để một thư viện tiêu chuẩn bận tâm khi sử dụng như vậy std::string, và tôi thực sự nghi ngờ rằng bạn có thể chứng minh một chuỗi COW hữu ích, hiệu quả đáp ứng các yêu cầu vô hiệu hóa C ++ 11. Vì vậy, tôi khẳng định rằng các noexceptthông số kỹ thuật đã được thêm vào phút cuối là hệ quả của việc cấm chuỗi COW, không phải lý do cơ bản. N2668 có vẻ hoàn toàn rõ ràng, tại sao bạn tiếp tục phủ nhận bằng chứng rõ ràng về ý định của ủy ban được vạch ra ở đó?
Jonathan Wakely

Ngoài ra, hãy nhớ rằng đó data()là một hàm thành viên const, vì vậy phải an toàn khi gọi đồng thời với các thành viên const khác, và ví dụ để gọi data()đồng thời với một luồng khác tạo bản sao của chuỗi. Vì vậy, bạn sẽ cần tất cả chi phí của mutex cho mọi hoạt động chuỗi, thậm chí là hằng số hoặc sự phức tạp của cấu trúc được tính tham chiếu có thể thay đổi không có khóa và sau tất cả, bạn chỉ nhận được chia sẻ nếu bạn không bao giờ sửa đổi hoặc truy cập chuỗi của bạn, rất nhiều, nhiều chuỗi sẽ có số tham chiếu là một. Vui lòng cung cấp mã, vui lòng bỏ qua noexceptbảo đảm.
Jonathan Wakely

2
Chỉ cần tập hợp một số mã với nhau bây giờ tôi phát hiện ra rằng có 129 basic_stringchức năng thành viên, cộng với các chức năng miễn phí. Chi phí trừu tượng: mã phiên bản 0 mới không được tối ưu hóa này chậm hơn từ 50 đến 100% với cả g ++ và MSVC. Nó không đảm bảo an toàn luồng ( shared_ptrtôi nghĩ là dễ dàng tận dụng ) và nó chỉ đủ để hỗ trợ phân loại từ điển cho mục đích định thời gian, nhưng lỗi modulo, nó chứng minh điểm rằng một tham chiếu được tính basic_stringlà được phép, ngoại trừ các noexceptyêu cầu C ++ . github.com/alfps/In-principle-demo-of-ref-counted-basic_string
Chúc mừng và hth. - Alf


20

CoW là một cơ chế chấp nhận được để tạo chuỗi nhanh hơn ... nhưng ...

nó làm cho mã đa luồng chậm hơn (tất cả những gì khóa để kiểm tra xem bạn có phải là người duy nhất viết giết hiệu suất khi sử dụng nhiều chuỗi hay không). Đây là lý do chính khiến CoW bị khai tử nhiều năm trước.

Các lý do khác là []nhà điều hành sẽ trả lại cho bạn dữ liệu chuỗi, mà không có bất kỳ biện pháp bảo vệ nào để bạn ghi đè lên chuỗi mà người khác mong đợi là không thay đổi. Điều tương tự cũng áp dụng cho c_str()data() .

Google nhanh chóng nói rằng đa luồng về cơ bản là lý do khiến nó không được phép (không rõ ràng).

Đề xuất nói:

Đề nghị

Chúng tôi đề xuất làm cho tất cả các hoạt động truy cập trình vòng lặp và phần tử có thể thực thi đồng thời một cách an toàn.

Chúng tôi đang tăng cường tính ổn định của các hoạt động ngay cả trong mã tuần tự.

Thay đổi này không cho phép triển khai copy-on-write một cách hiệu quả.

theo dõi bởi

Tổn thất tiềm năng lớn nhất về hiệu suất do chuyển đổi khỏi triển khai sao chép-ghi là tiêu thụ bộ nhớ tăng lên cho các ứng dụng có các chuỗi chủ yếu là đọc rất lớn. Tuy nhiên, chúng tôi tin rằng đối với các ứng dụng đó, dây thừng là giải pháp kỹ thuật tốt hơn và đề xuất dây thừng được xem xét để đưa vào Thư viện TR2.

Dây thừng là một phần của STLPort và SGIs STL.


2
Vấn đề nhà điều hành [] không thực sự là một vấn đề. Biến thể const cung cấp khả năng bảo vệ và biến thể không phải const luôn có tùy chọn thực hiện CoW tại thời điểm đó (hoặc thực sự điên rồ và thiết lập lỗi trang để kích hoạt nó).
Christopher Smith

+1 Đi đến các vấn đề.
Chúc mừng và hth. - Alf

5
thật ngớ ngẩn khi một lớp std :: cow_string không được bao gồm, với lock_buffer (), v.v. Có rất nhiều lần tôi biết việc phân luồng không phải là một vấn đề. thực tế là thường xuyên hơn không.
Erik Aronesty

Tôi thích gợi ý về một giải pháp thay thế, tức là dây thừng. Tôi tự hỏi liệu có bất kỳ loại và triển khai lựa chọn thay thế nào khác không.
Voltaire

5

Từ 21.4.2 các hàm tạo basic_string và toán tử gán [string.cons]

basic_string(const basic_string<charT,traits,Allocator>& str);

[...]

2 Hiệu ứng : Xây dựng một đối tượng của lớp basic_stringnhư được chỉ ra trong Bảng 64. [...]

Bảng 64 tài liệu hữu ích rằng sau khi xây dựng một đối tượng thông qua hàm tạo (bản sao) này, this->data()có giá trị như sau:

trỏ vào phần tử đầu tiên của bản sao được cấp phát của mảng có phần tử đầu tiên được trỏ tới bởi str.data ()

Có các yêu cầu tương tự đối với các hàm tạo tương tự khác.


+1 Giải thích cách C ++ 11 (ít nhất một phần) cấm COW.
Chúc mừng và hth. - Alf

Xin lỗi, tôi đã mệt. Nó không giải thích bất cứ điều gì hơn là một lệnh gọi .data () phải kích hoạt sao chép COW nếu bộ đệm hiện được chia sẻ. Vẫn là thông tin hữu ích nên tôi vẫn để ý kiến ​​ủng hộ.
Chúc mừng và hth. - Alf

1

Vì hiện tại đã được đảm bảo rằng các chuỗi được lưu trữ liền kề và giờ đây bạn được phép đưa một con trỏ đến bộ nhớ trong của một chuỗi, (tức là & str [0] hoạt động giống như đối với một mảng), nên không thể tạo COW hữu ích thực hiện. Bạn sẽ phải tạo một bản sao cho quá nhiều thứ. Ngay cả khi chỉ sử dụng operator[]hoặc begin()trên một chuỗi không phải const cũng sẽ yêu cầu một bản sao.


1
Tôi nghĩ rằng các chuỗi trong C ++ 11 được đảm bảo sẽ được lưu trữ liền kề.
mfontanini

4
Trong quá khứ bạn đã phải làm bản sao trong tất cả những tình huống và nó không phải là một vấn đề ...
David Rodríguez - dribeas

@mfontanini có, nhưng họ không phải là trước đây
Dirk Holsopple

3
Mặc dù C ++ 11 đảm bảo các chuỗi là liền nhau, nhưng điều đó là trực giao với việc cấm các chuỗi COW. Chuỗi COW của GCC là liền nhau, do đó, rõ ràng tuyên bố của bạn rằng "không thể thực hiện triển khai COW hữu ích" là không có thật.
Jonathan Wakely

1
@supercat, yêu cầu lưu trữ sao lưu (ví dụ: bằng cách gọi c_str()) phải là O (1) và không thể ném, và không được giới thiệu các chủng tộc dữ liệu, vì vậy rất khó đáp ứng các yêu cầu đó nếu bạn ghép nối lười biếng. Trong thực tế, lựa chọn hợp lý duy nhất là luôn lưu trữ dữ liệu liền kề.
Jonathan Wakely

1

Là BÒ basic_string bị cấm trong C ++ 11 trở lên không?

Về

Tôi có đúng là C ++ 11 không thừa nhận việc triển khai dựa trên COW của std::string?

Đúng.

Về

Nếu vậy, hạn chế này có được nêu rõ ràng ở đâu đó trong tiêu chuẩn mới (ở đâu) không?

Gần như trực tiếp, bởi các yêu cầu về độ phức tạp liên tục đối với một số hoạt động yêu cầusao chép vật lýO ( n ) dữ liệu chuỗi trong quá trình triển khai COW.

Ví dụ, đối với các chức năng thành viên

auto operator[](size_type pos) const -> const_reference;
auto operator[](size_type pos) -> reference;

… Mà trong quá trình triển khai COW sẽ ¹thể kích hoạt sao chép dữ liệu chuỗi để hủy chia sẻ giá trị chuỗi, tiêu chuẩn C ++ 11 yêu cầu

C ++ 11 §21.4.5 / 4 :

”Độ phức tạp: thời gian không đổi.

… Loại trừ việc sao chép dữ liệu như vậy, và do đó, COW.

C ++ 03 được hỗ trợ triển khai COW bởi không có những yêu cầu liên tục phức tạp, và bởi, trong điều kiện hạn chế nhất định, cho phép các cuộc gọi đến operator[](), at(), begin(), rbegin(), end(), hoặc rend()để tham khảo vô hiệu, con trỏ và lặp đề cập đến các mặt hàng chuỗi, tức là để có thể phải chịu một Sao chép dữ liệu BÒ. Hỗ trợ này đã bị loại bỏ trong C ++ 11.


COW có bị cấm thông qua các quy tắc hủy bỏ hiệu lực C ++ 11 không?

Trong một câu trả lời khác tại thời điểm viết bài được chọn làm giải pháp và được nhiều người ủng hộ và do đó dường như được tin tưởng, nó khẳng định rằng

Đối với chuỗi COW, việc gọi non- const operator[]sẽ yêu cầu tạo một bản sao (và làm mất hiệu lực tham chiếu), điều này không được phép trong đoạn [trích dẫn] ở trên [C ++ 11 §21.4.1 / 6]. Do đó, không còn hợp pháp khi có chuỗi COW trong C ++ 11.

Khẳng định đó không chính xác và gây hiểu lầm theo hai cách chính:

  • Nó chỉ ra không chính xác rằng chỉ những người truy cập không phải constmặt hàng mới cần kích hoạt sao chép dữ liệu COW.
    Nhưng người truy cập constmục cũng cần kích hoạt sao chép dữ liệu, vì chúng cho phép mã máy khách tạo thành tham chiếu hoặc con trỏ (trong C ++ 11) nó không được phép làm mất hiệu lực sau này thông qua các hoạt động có thể kích hoạt sao chép dữ liệu COW.
  • Nó giả định không chính xác rằng việc sao chép dữ liệu COW có thể làm mất hiệu lực tham chiếu.
    Nhưng trong một triển khai đúng, việc sao chép dữ liệu COW, việc hủy chia sẻ giá trị chuỗi, được thực hiện tại một điểm trước khi có bất kỳ tham chiếu nào có thể bị vô hiệu.

Để xem cách triển khai COW đúng trong C ++ 11 basic_stringsẽ hoạt động như thế nào , khi các yêu cầu O (1) làm cho điều này không hợp lệ bị bỏ qua, hãy nghĩ về một triển khai trong đó một chuỗi có thể chuyển đổi giữa các chính sách quyền sở hữu. Một cá thể chuỗi bắt đầu với chính sách Có thể chia sẻ. Với chính sách này hoạt động, không thể có tham chiếu mặt hàng bên ngoài. Phiên bản có thể chuyển đổi sang chính sách Unique và nó phải làm như vậy khi một tham chiếu mục có khả năng được tạo, chẳng hạn như với lệnh gọi tới.c_str() (ít nhất là nếu điều đó tạo ra một con trỏ tới bộ đệm nội bộ). Trong trường hợp chung của nhiều trường hợp chia sẻ quyền sở hữu giá trị, điều này đòi hỏi phải sao chép dữ liệu chuỗi. Sau quá trình chuyển đổi đó sang chính sách Duy nhất, cá thể chỉ có thể chuyển trở lại Có thể chia sẻ bằng một thao tác làm vô hiệu tất cả các tham chiếu, chẳng hạn như chuyển nhượng.

Vì vậy, trong khi kết luận của câu trả lời đó, rằng chuỗi COW bị loại trừ, là đúng, thì lý do được đưa ra là không chính xác và gây hiểu lầm nghiêm trọng.

Tôi nghi ngờ nguyên nhân của sự hiểu lầm này là một lưu ý không chuẩn mực trong phụ lục C của C ++ 11:

C ++ 11 §C.2.11 [diff.cpp03.strings], về §21.3:

Thay đổi : basic_stringcác yêu cầu không còn cho phép các chuỗi được tính tham chiếu
Lý do: Sự hợp lệ khác biệt một cách tinh tế với các chuỗi được tính tham chiếu. Thay đổi này quy định hành vi (sic) đối với tiêu chuẩn này.
Ảnh hưởng đến tính năng gốc: Mã C ++ 2003 hợp lệ có thể thực thi khác trong tiêu chuẩn này

Đây là lý do giải thích lý do chính tại sao người ta quyết định loại bỏ hỗ trợ COW đặc biệt C ++ 03. Cơ sở lý do này không phải là cách tiêu chuẩn ngăn cản việc thực hiện COW một cách hiệu quả. Tiêu chuẩn không cho phép COW thông qua các yêu cầu O (1).

Tóm lại, các quy tắc vô hiệu hóa C ++ 11 không loại trừ việc triển khai COW std::basic_string. Nhưng họ loại trừ một cách triển khai COW kiểu C ++ 03 không hạn chế, hiệu quả hợp lý giống như cách triển khai trong ít nhất một trong các triển khai thư viện tiêu chuẩn của g ++. Hỗ trợ C ++ 03 COW đặc biệt cho phép hiệu quả thực tế, đặc biệt là sử dụng các trình truy cập constmục, với chi phí là các quy tắc phức tạp, phức tạp để vô hiệu hóa:

C ++ 03 §21.3 / 5 bao gồm hỗ trợ COW “cuộc gọi đầu tiên”:

Các tham chiếu, con trỏ và trình vòng lặp tham chiếu đến các phần tử của một basic_stringchuỗi có thể bị vô hiệu bởi các cách sử dụng sau của basic_stringđối tượng đó :
- Làm đối số cho các hàm không phải là thành viên swap()(21.3.7.8), operator>>()(21.3.7.9) và getline()(21.3. 7.9).
- Như một lý lẽ để basic_string::swap().
- Chức năng gọi điện data()c_str()thành viên.
- Kêu gọi phi constchức năng thành viên, trừ operator[](), at(), begin(), rbegin(), end(), và rend().
- Tiếp theo cho bất kỳ trong những ứng dụng trên ngoại trừ các hình thức insert()erase()đó trở lại vòng lặp, cuộc gọi đầu tiên để phi constchức năng thành viên operator[](), at(), begin(), , hoặcrbegin() ,end()rend().

Những quy tắc này phức tạp và tinh vi đến nỗi tôi nghi ngờ nhiều lập trình viên, nếu có, có thể đưa ra một bản tóm tắt chính xác. Tôi không thể.


Điều gì sẽ xảy ra nếu các yêu cầu O (1) bị bỏ qua?

Nếu các yêu cầu về thời gian không đổi trong C ++ 11 đối với ví dụ operator[]được bỏ qua, thì COW for basic_stringcó thể khả thi về mặt kỹ thuật, nhưng khó thực hiện.

Các hoạt động có thể truy cập nội dung của một chuỗi mà không phải sao chép dữ liệu COW bao gồm:

  • Kết nối qua +.
  • Đầu ra thông qua <<.
  • Sử dụng một basic_stringđối số làm đối số cho các hàm thư viện tiêu chuẩn.

Cái sau vì thư viện chuẩn được phép dựa trên kiến ​​thức và cấu trúc cụ thể để triển khai.

Ngoài ra, một triển khai có thể cung cấp các chức năng không chuẩn khác nhau để truy cập nội dung chuỗi mà không kích hoạt sao chép dữ liệu COW.

Một yếu tố phức tạp chính là trong C ++ 11, basic_stringquyền truy cập mục phải kích hoạt sao chép dữ liệu (hủy chia sẻ dữ liệu chuỗi) nhưng được yêu cầu không ném , ví dụ C ++ 11 §21.4.5 / 3 “ Throws: Nothing.”. Và vì vậy nó không thể sử dụng phân bổ động thông thường để tạo một bộ đệm mới cho việc sao chép dữ liệu COW. Một cách giải quyết vấn đề này là sử dụng một heap đặc biệt, nơi có thể dự trữ mà không thực sự được cấp phát, và sau đó dự trữ số tiền cần thiết cho mỗi tham chiếu logic đến giá trị chuỗi. Dự trữ và hủy đặt trước trong một đống như vậy có thể là thời gian không đổi, O (1) và phân bổ số tiền mà một người đã đặt trước, có thể bộ nhớ noexcept. Để tuân thủ các yêu cầu của tiêu chuẩn, với cách tiếp cận này, có vẻ như cần phải có một đống đặc biệt dựa trên đặt trước cho mỗi người phân bổ riêng biệt.


Lưu ý:
¹ Trình truy cập constmặt hàng kích hoạt sao chép dữ liệu BÒ vì nó cho phép mã khách hàng nhận được tham chiếu hoặc con trỏ đến dữ liệu, mà mã này không được phép làm mất hiệu lực bởi việc sao chép dữ liệu sau đó được kích hoạt bởi ví dụ: trình truy cập không phải constmặt hàng.


3
" Ví dụ của bạn là một ví dụ điển hình về việc triển khai sai cho C ++ 11. Có thể nó đúng cho C ++ 03." Vâng , đó là điểm của ví dụ . Nó hiển thị một chuỗi COW hợp pháp trong C ++ 03 vì nó không phá vỡ các quy tắc vô hiệu trình lặp cũ và không hợp pháp trong C ++ 11 vì nó phá vỡ các quy tắc vô hiệu trình lặp mới. Và nó cũng mâu thuẫn với tuyên bố mà tôi đã trích dẫn trong bình luận ở trên.
Jonathan Wakely,

2
Nếu bạn muốn nói thể chia sẻ không ban đầu chia sẻ tôi sẽ không tranh luận. Nói điều gì đó được chia sẻ ban đầu chỉ là khó hiểu. Được chia sẻ với chính nó? Đó không phải là ý nghĩa của từ này. Nhưng tôi nhắc lại: nỗ lực của bạn để lập luận rằng các quy tắc vô hiệu hóa trình lặp C ++ 11 không cấm một số chuỗi COW giả định chưa bao giờ được sử dụng trong thực tế (và sẽ có hiệu suất không thể chấp nhận được), khi chúng chắc chắn cấm loại chuỗi COW đã được sử dụng trong thực tế, hơi mang tính hàn lâm và vô nghĩa.
Jonathan Wakely,

5
Chuỗi COW được đề xuất của bạn rất thú vị, nhưng tôi không chắc nó sẽ hữu ích như thế nào . Điểm của một chuỗi COW là chỉ sao chép dữ liệu chuỗi trong trường hợp hai chuỗi được ghi vào. Việc triển khai đề xuất của bạn yêu cầu sao chép khi bất kỳ thao tác đọc nào do người dùng xác định xảy ra. Ngay cả khi trình biên dịch biết chỉ đọc của nó, nó vẫn phải sao chép. Hơn nữa, việc sao chép một chuỗi Unique sẽ dẫn đến việc sao chép dữ liệu chuỗi của nó (có lẽ là ở trạng thái Có thể chia sẻ), điều này một lần nữa khiến COW trở nên vô nghĩa. Vì vậy, nếu không có sự đảm bảo về độ phức tạp, bạn có thể viết ... một chuỗi COW thực sự khó hiểu .
Nicol Bolas,

2
Vì vậy, trong khi bạn nói đúng về mặt kỹ thuật rằng sự phức tạp đảm bảo là thứ ngăn cản bạn viết bất kỳ dạng COW nào, thì [basic.string] / 5 thực sự ngăn bạn viết bất kỳ dạng chuỗi COW thực sự hữu ích nào .
Nicol Bolas

4
@JonathanWakely: (1) Trích dẫn của bạn không phải là câu hỏi. Đây là câu hỏi: “Tôi có đúng là C ++ 11 không thừa nhận việc triển khai dựa trên COW của std :: string không? Nếu vậy, hạn chế này có được nêu rõ ràng ở đâu đó trong tiêu chuẩn mới (ở đâu) không? ” (2) Ý kiến ​​của bạn rằng COW std::string, khi bỏ qua các yêu cầu O (1), sẽ không hiệu quả, là ý kiến ​​của bạn. Tôi không biết hiệu suất có thể ra sao, nhưng tôi nghĩ rằng khẳng định đó được đưa ra nhiều hơn cho cảm giác của nó, cho những rung cảm mà nó truyền tải, hơn là bất kỳ sự liên quan nào đến câu trả lời này.
Chúc mừng và hth. - Alf

0

Tôi luôn tự hỏi về những con bò bất biến: một khi con bò được tạo ra, tôi chỉ có thể được thay đổi thông qua việc chuyển nhượng từ một con bò khác, do đó nó sẽ tuân thủ tiêu chuẩn.

Hôm nay tôi đã có thời gian để thử nó cho một bài kiểm tra so sánh đơn giản: một bản đồ kích thước N được khóa bằng chuỗi / con bò với mỗi nút chứa một tập hợp tất cả các chuỗi trong bản đồ (chúng ta có số đối tượng NxN).

Với các chuỗi có kích thước ~ 300 byte và N = 2000 bò nhanh hơn một chút và sử dụng bộ nhớ ít hơn gần như theo thứ tự độ lớn. Xem bên dưới, kích thước tính bằng kbs, chạy b tính bằng bò.

~/icow$ ./tst 2000
preparation a
run
done a: time-delta=6 mem-delta=1563276
preparation b
run
done a: time-delta=3 mem-delta=186384
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.