Việc xóa bản sao được phép xảy ra trong một số trường hợp. Tuy nhiên, ngay cả khi nó được cho phép, mã vẫn phải có thể hoạt động như thể bản sao chưa được giải thích. Cụ thể, phải có một bản sao có thể truy cập và / hoặc phương thức khởi tạo di chuyển.
Bản sao bảo đảm elision xác định lại một số khái niệm C ++, sao cho một số trường hợp nhất định trong đó các bản sao / di chuyển có thể được giải quyết không thực sự gây ra một bản sao / di chuyển nào cả . Trình biên dịch không tạo ra một bản sao; tiêu chuẩn nói rằng không bao giờ có thể xảy ra việc sao chép như vậy.
Hãy xem xét chức năng này:
T Func() {return T();}
Theo quy tắc xóa bản sao không được đảm bảo, điều này sẽ tạo một giá trị tạm thời, sau đó chuyển từ tạm thời đó sang giá trị trả về của hàm. Thao tác di chuyển đó có thể được giải quyết, nhưng T
vẫn phải có một phương thức khởi tạo di chuyển có thể truy cập ngay cả khi nó chưa bao giờ được sử dụng.
Tương tự:
T t = Func();
Đây là bản sao khởi tạo của t
. Điều này sẽ sao chép khởi tạo t
với giá trị trả về Func
. Tuy nhiên, T
vẫn phải có một phương thức khởi tạo di chuyển, mặc dù nó sẽ không được gọi.
Bản sao bảo đảm elision xác định lại ý nghĩa của một biểu thức prvalue . Pre-C ++ 17, prvalues là các đối tượng tạm thời. Trong C ++ 17, một biểu prvalue chỉ đơn thuần là một cái gì đó mà có thể hiện thực một tạm thời, nhưng nó không phải là một tạm thời được nêu ra.
Nếu bạn sử dụng prvalue để khởi tạo một đối tượng thuộc kiểu của prvalue, thì không có tạm thời nào được thực hiện. Khi bạn làm như vậy return T();
, điều này sẽ khởi tạo giá trị trả về của hàm thông qua một prvalue. Vì hàm đó trả về T
nên không có tạm thời nào được tạo; việc khởi tạo prvalue chỉ đơn giản là trực tiếp khởi tạo giá trị trả về.
Điều cần hiểu là, vì giá trị trả về là prvalue nên nó chưa phải là một đối tượng . Nó chỉ là một bộ khởi tạo cho một đối tượng, giống như vậy T()
.
Khi bạn làm như vậy T t = Func();
, prvalue của giá trị trả về sẽ trực tiếp khởi tạo đối tượng t
; không có giai đoạn "tạo tạm thời và sao chép / di chuyển". Vì Func()
giá trị trả về của là một prvalue tương đương với T()
, t
được khởi tạo trực tiếp bởi T()
, chính xác như thể bạn đã làm T t = T()
.
Nếu một prvalue được sử dụng theo bất kỳ cách nào khác, prvalue sẽ hiện thực hóa một đối tượng tạm thời, đối tượng này sẽ được sử dụng trong biểu thức đó (hoặc bị loại bỏ nếu không có biểu thức). Vì vậy, nếu bạn đã làm vậy const T &rt = Func();
, prvalue sẽ hiện thực hóa một tệp tạm thời (sử dụng T()
làm trình khởi tạo), tham chiếu của nó sẽ được lưu trữ trong đó rt
, cùng với nội dung mở rộng vòng đời tạm thời thông thường.
Một điều được đảm bảo elision cho phép bạn làm là trả về các đối tượng bất động. Ví dụ: lock_guard
không thể sao chép hoặc di chuyển, vì vậy bạn không thể có một hàm trả về nó theo giá trị. Nhưng với đảm bảo tách bản sao, bạn có thể.
Sự tách rời đảm bảo cũng hoạt động với quá trình khởi tạo trực tiếp:
new T(FactoryFunction());
Nếu FactoryFunction
trả về T
theo giá trị, biểu thức này sẽ không sao chép giá trị trả về vào bộ nhớ được cấp phát. Thay vào đó, nó sẽ cấp phát bộ nhớ và sử dụng bộ nhớ được cấp phát làm bộ nhớ giá trị trả về cho lệnh gọi hàm trực tiếp.
Vì vậy, các hàm gốc trả về theo giá trị có thể trực tiếp khởi tạo bộ nhớ được cấp phát heap mà không cần biết về nó. Tất nhiên, miễn là các chức năng này tuân theo các quy tắc nội bộ của việc tách bản sao được đảm bảo. Họ phải trả về một loại giá trị prvalue T
.
Tất nhiên, điều này cũng hoạt động:
new auto(FactoryFunction());
Trong trường hợp bạn không thích viết tên kiểu chữ.
Điều quan trọng là phải nhận ra rằng các đảm bảo trên chỉ hoạt động đối với các giá trị tương đương. Đó là, bạn không có gì đảm bảo khi trả về một biến được đặt tên :
T Func()
{
T t = ...;
...
return t;
}
Trong trường hợp này, t
vẫn phải có một hàm tạo sao chép / di chuyển có thể truy cập được. Có, trình biên dịch có thể chọn để tối ưu hóa việc sao chép / di chuyển. Nhưng trình biên dịch vẫn phải xác minh sự tồn tại của một hàm tạo sao chép / di chuyển có thể truy cập được.
Vì vậy, không có gì thay đổi đối với tối ưu hóa giá trị trả về được đặt tên (NRVO).