2012-03-15 31 views
7

Tôi đã có một API mà trông như thế này:C++ 11: Làm các ngữ nghĩa di chuyển có liên quan đến việc truyền theo giá trị không?

void WriteDefaultFileOutput(std::wostream &str, std::wstring target) 
{ 
    //Some code that modifies target before printing it and such... 
} 

Tôi tự hỏi nếu nó muốn được hợp lý để cho phép di chuyển ngữ nghĩa bằng cách làm này:

void WriteDefaultFileOutput(std::wostream &str, std::wstring&& target) 
{ 
    //As above 
} 
void WriteDefaultFileOutput(std::wostream &str, std::wstring const& target) 
{ 
    std::wstring tmp(target); 
    WriteDefaultFileOutput(str, std::move(tmp)); 
} 

hoặc là này chỉ soạn sẵn rằng trình biên dịch có thể tìm ra được không?

+0

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? –

+0

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

+0

Đâ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

Trả lời

15

"Truyền theo giá trị" có thể có nghĩa là sao chép hoặc di chuyển; nếu đối số là một giá trị, thì hàm tạo bản sao được gọi. Nếu đối số là một rvalue, hàm khởi tạo di chuyển được gọi. Bạn không phải làm bất cứ điều gì đặc biệt liên quan đến tài liệu tham khảo rvalue để có được di chuyển ngữ nghĩa với đi qua giá trị.

tôi đào sâu vào chủ đề này trong What are move semantics?

+0

Trong trường hợp vượt qua theo giá trị, và một đối số rvalue, tôi mong đợi tạm thời được xây dựng chính xác nơi nó cần thiết, do đó sẽ không có bất cứ điều gì để di chuyển. Hoặc để sao chép, trong các trình biên dịch cũ hơn. –

+0

Có thể; đất tối ưu hóa trình biên dịch không phải là lãnh thổ của tôi. – fredoverflow

+2

Nó hầu như không những gì tôi sẽ gọi tối ưu hóa (mặc dù chính thức nó được). Việc chỉ định một hàm tạo bản sao có thể dẫn đến thay đổi ngữ nghĩa chương trình, chỉ hợp pháp vì tiêu chuẩn cho phép trình biên dịch ủy quyền rõ ràng để làm điều đó, và bởi vì nó có thể thay đổi ngữ nghĩa, phải được tính đến khi đánh giá chính xác chương trình. –

2

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 &&:

  1. Bạn đang viết một constructor di chuyển.
  2. 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 để 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ừ đó.

Các vấn đề liên quan