Bạn nên thích pass-by-value nếu bạn đang đi để tạo một bản sao có giá trị không thể di chuyển. Ví dụ: const&
phiên bản WriteDefaultFileOutput
sao chép một cách rõ ràng thông số, sau đó di chuyển bản sao bằng phiên bản di chuyển. Điều này có nghĩa là nếu WriteDefaultFileOutput
được gọi với giá trị di động (xvalue hoặc prvalue), thì nó sẽ được di chuyển, và nếu nó được gọi với một giá trị, nó sẽ được sao chép.
Điều này có nghĩa là có không có khác biệt giữa hai dạng của hàm này. Hãy xem xét điều này:
WriteDefaultFileOutput(L"SomeString");
Trong trường hợp đầu tiên, nó sẽ tạo tạm thời wstring
. Thời gian là giá trị, vì vậy nó sẽ được "di chuyển" vào tham số (vì wstring
có một hàm khởi tạo). Tất nhiên, bất kỳ trình biên dịch nào có giá trị muối của nó sẽ bỏ qua di chuyển và chỉ đơn giản là xây dựng tạm thời trực tiếp vào tham số.
Trong trường hợp thứ hai của bạn, nhiều hơn hoặc ít hơn cùng một điều xảy ra. Tạm thời wstring
được tạo. Thời gian là giá trị, vì vậy chúng có thể liên kết với các loại tham số &&
. Do đó, nó sẽ gọi phiên bản đầu tiên của hàm của bạn với một tham chiếu giá trị r cho tạm thời. Sự khác biệt duy nhất có thể là từ một trình biên dịch sẽ không bỏ qua bước di chuyển. Và thậm chí sau đó, một động thái không phải là đắt tiền (tùy thuộc vào thực hiện basic_string
của bạn).
Bây giờ xem xét việc này:
std::wstring myStr{L"SomeString"};
WriteDefaultFileOutput(myStr);
Trong trường hợp đầu tiên, các cuộc gọi đến WriteDefaultFileOutput
sẽ gây ra một bản sao của giá trị myStr
vào tham số chức năng.
Trong trường hợp thứ hai, myStr
là một giá trị. Nó không thể liên kết với thông số &&
. Do đó, phiên bản duy nhất nó có thể gọi là phiên bản const&
. Chức năng đó sẽ tự xây dựng một bản sao, và sau đó di chuyển bản sao bằng một bản sao khác.
Một hiệu ứng giống hệt nhau. Phiên bản đầu tiên của bạn có ít mã hơn, vì vậy hãy đi với điều đó vì lý do rõ ràng.
Nói chung, tôi sẽ nói rằng chỉ có hai lý do để có một tham số như một &&
:
- Bạn đang viết một constructor di chuyển.
- Bạn đang viết chức năng chuyển tiếp và cần sử dụng chuyển tiếp hoàn hảo.
Trong tất cả các trường hợp khác mà bạn muốn chuyển động có thể, chỉ cần lấy một giá trị. Nếu người dùng muốn sao chép, hãy để họ sao chép. Tôi cho rằng nếu bạn muốn cấm sao chép một cách rõ ràng thông số, bạn có thể lấy &&
. Nhưng vấn đề chính là một trong những điều rõ ràng.
Nếu bạn lấy thông số giá trị và người dùng cung cấp giá trị có thể di chuyển, thì giá trị do người dùng cung cấp sẽ luôn di chuyển. Ví dụ: phiên bản &&
của WriteDefaultFileOutput
không có để thực sự di chuyển dữ liệu từ thông số của nó. Nó chắc chắn có thể. Nhưng nó không phải. Nếu nó lấy một giá trị, thì nó sẽ đã yêu cầu dữ liệu.
Do đó, nếu một hàm có tham số giá trị và bạn thấy giá trị std::move
, thì bạn biết rằng đối tượng đã được di chuyển hiện đang trống. Đó là đảm bảo đã được di chuyển từ đó.
Wstring có một hàm tạo di chuyển, vì vậy không phải điều này đang xảy ra chưa? –
Bạn không thể tránh tmp và std :: di chuyển và vượt qua chuỗi (mục tiêu) trong cuộc gọi hàm trực tiếp? – juanchopanza
Đây có thể là một câu hỏi ngu ngốc, nhưng tại sao lại đi qua một tham chiếu const bình thường không phải là một lựa chọn? Viết một cái gì đó (ở đây: một chuỗi) vào một luồng không nên sửa đổi nó, cũng không yêu cầu một bản sao riêng tư, bổ sung. Vì vậy, tôi thực sự không thấy lý do đằng sau lần đầu tiên tạo một bản sao thời gian (có thể sâu) và sau đó sử dụng ngữ nghĩa di chuyển (phá hoại) trên tạm thời. Ngoại trừ vì lợi ích của việc sử dụng ngữ nghĩa di chuyển, tất nhiên. – Damon