Câu trả lời được chấp nhận là câu trả lời hay (và tôi đã upvoted nó). Nhưng tôi muốn giải quyết câu hỏi này chi tiết hơn một chút:
Cốt lõi của câu hỏi của tôi là: Tại sao nó không tự động chuyển nhượng nhiệm vụ ? Trình biên dịch biết v không được sử dụng sau khi chuyển nhượng , phải không? Hoặc không C++ 11 không yêu cầu trình biên dịch phải là thông minh?
Khả năng này được xem xét trong quá trình thiết kế ngữ nghĩa di chuyển.Ở mức độ cao nhất, bạn có thể muốn trình biên dịch thực hiện một số phân tích tĩnh và di chuyển từ các đối tượng bất cứ khi nào có thể:
void set_name(std::string v)
{
name_ = v; // move from v if it can be proven that some_event is false?
if (some_event)
f(v);
}
Cuối cùng yêu cầu loại phân tích này từ trình biên dịch rất phức tạp. Một số trình biên dịch có thể làm bằng chứng, và một số khác thì không. Do đó dẫn đến mã không thực sự di động.
Ok, vậy còn một số trường hợp đơn giản hơn nếu không có báo cáo thì sao?
void foo()
{
X x;
Y y(x);
X x2 = x; // last use? move?
}
Vâng, rất khó để biết nếu y.~Y()
sẽ thấy x
đã được chuyển từ. Và nói chung:
void foo()
{
X x;
// ...
// lots of code here
// ...
X x2 = x; // last use? move?
}
nó là khó khăn đối với trình biên dịch để phân tích này để biết liệu x
là thực sự không còn được sử dụng sau khi xây dựng bản sao cho x2
.
Vì vậy, bản gốc "di chuyển" đề nghị đã đưa ra một quy tắc cho di chuyển ngầm rằng thật sự rất đơn giản, và rất bảo thủ:
lvalues chỉ có thể được ngầm chuyển từ trong trường hợp sao chép sự bỏ bớt là đã cho phép.
Ví dụ:
#include <cassert>
struct X
{
int i_;
X() : i_(1) {}
~X() {i_ = 0;}
};
struct Y
{
X* x_;
Y() : x_(0) {}
~Y() {assert(x_ != 0); assert(x_->i_ != 0);}
};
X foo(bool some_test)
{
Y y;
X x;
if (some_test)
{
X x2;
return x2;
}
y.x_ = &x;
return x;
}
int main()
{
X x = foo(false);
}
Ở đây, bằng C++ 98/03 quy tắc, chương trình này có thể hoặc có thể không khẳng định, tuỳ thuộc vào việc hay không sao chép sự bỏ bớt tại return x
xảy ra. Nếu nó xảy ra, chương trình chạy tốt. Nếu nó không xảy ra, chương trình sẽ khẳng định.
Và do đó, nó đã được lý luận: Khi RVO được cho phép, chúng tôi đã ở trong một khu vực không có bảo đảm về giá trị của x
. Vì vậy, chúng ta sẽ có thể tận dụng lợi thế của việc này và di chuyển từ x
. Nguy cơ có vẻ nhỏ và lợi ích được xem là lớn. Điều này không chỉ có nghĩa là nhiều chương trình hiện có sẽ trở nên nhanh hơn nhiều với một biên dịch lại đơn giản, nhưng nó cũng có nghĩa là bây giờ chúng ta có thể trả về các kiểu "di chuyển" từ các chức năng của nhà máy. Đây là một lợi ích rất lớn đối với tỷ lệ rủi ro.
Cuối quá trình chuẩn hóa, chúng tôi có chút tham lam và cũng cho rằng di chuyển ngầm xảy ra khi trả về tham số giá trị (và loại khớp với kiểu trả về). Những lợi ích có vẻ tương đối lớn ở đây quá, mặc dù cơ hội phá vỡ mã là lớn hơn một chút vì đây không phải là trường hợp RVO (hoặc là) hợp pháp. Nhưng tôi không có một cuộc biểu tình phá mã cho trường hợp này.
Vì vậy, cuối cùng, câu trả lời cho câu hỏi cốt lõi của bạn là thiết kế ban đầu của ngữ nghĩa di chuyển đã có một lộ trình rất bảo thủ đối với việc phá vỡ mã hiện có. Nếu không, nó chắc chắn sẽ bị bắn hạ trong ủy ban. Cuối quá trình này, đã có một vài thay đổi khiến thiết kế tích cực hơn một chút. Nhưng vào thời điểm này, đề xuất cốt lõi đã được củng cố vững chắc trong tiêu chuẩn với sự hỗ trợ đa số (nhưng không nhất trí).
Tôi không thấy một hàm tạo di chuyển ở bất kỳ đâu? – Corbin
@NicolBolas: Điều đó không liên quan đến từ xa đối với câu hỏi này. Anh ấy hỏi về việc di chuyển một thành viên 'std :: string' của lớp mình. Anh ấy không cố gắng di chuyển lớp. –
@MooingDuck: Oh. Vâng, không bao giờ tâm trí sau đó. Tôi thực sự không thể làm gì nhiều về cuộc bầu cử gần đây ... –