2013-03-28 16 views
23

Xét sau:Move ngữ nghĩa & tranh luận để đánh giá

std::string make_what_string(const std::string &id); 

struct basic_foo 
{ 
    basic_foo(std::string message, std::string id); 
}; 

struct foo 
    : public basic_foo 
{ 
    foo::foo(std::string id) 
     : basic_foo(make_what_string(id), std::move(id)) // Is this valid? 
    { 
    } 
}; 

Vì đơn đặt hàng đánh giá tham số trong C++ là không xác định, tôi tự hỏi nếu dòng

basic_foo(make_what_string(id), std::move(id)) 

trong mã trên là hợp lệ.

Tôi biết rằng std::move không có gì khác ngoài dàn diễn viên, nhưng khi nào lệnh std :: string di chuyển ctor được thực thi? Sau khi tất cả các đối số đã được đánh giá và đã đến lúc gọi hàm tạo cơ sở? Hoặc là điều này được thực hiện trong quá trình đánh giá các tham số? Trong Nói cách khác:

Liệu trình biên dịch thực hiện điều này:

std::string &&tmp2 = std::move(id); 
std::string tmp1 = make_what_string(id); 
basic_foo(tmp1, tmp2); 

đó là hợp lệ. Hoặc điều này:

std::string tmp2 = std::move(id); 
std::string tmp1 = make_what_string(id); 
basic_foo(tmp1, tmp2); 

không hợp lệ. Lưu ý rằng trong cả hai trường hợp, thứ tự là "bất ngờ" .

+0

Thực ra, mã này hợp lệ. Tuy nhiên, tôi tin rằng bạn có nghĩa là để có chuỗi 'id' trong constructor' base_foo' bởi tham chiếu rvalue chứ không phải bởi giá trị (không bạn?). –

+1

@CassioNeri, Không có mã như dự định :) – Tom

+0

Vâng, tôi nhận ra rằng trong giờ ăn trưa. :-) Câu hỏi rất hay. –

Trả lời

16

Xem phần 1.9:

Trừ khi có ghi chú, đánh giá các toán hạng của các toán tử riêng lẻ và các biểu thức con của các biểu thức riêng lẻ không được kết thúc.

Khi gọi một hàm (có hoặc không có chức năng là inline), mỗi giá trị tính toán và phụ ảnh hưởng kết hợp với bất kỳ biểu hiện lý luận, hoặc với biểu thức postfix chỉ định hàm được gọi, được được sắp xếp trước khi thực hiện mọi biểu thức hoặc câu lệnh trong phần thân của hàm được gọi. [Lưu ý: Tính toán giá trị và tác dụng phụ liên quan đến các biểu thức đối số khác nhau không được kết nối. - cuối note]

Tôi nghĩ vấn đề là nó không phải là rất rõ liệu khởi tạo các thông số được coi là một tác dụng phụ liên quan đến các biểu thức luận. Tuy nhiên, nó dường như được sao lưu theo phần 5.2.2:

Việc khởi tạo và hủy mỗi thông số xảy ra trong bối cảnh chức năng gọi .

Và đó cũng là một lưu ý trong đoạn tương tự mà làm cho nó một ít rõ ràng hơn:

Khi một hàm được gọi, mỗi tham số (8.3.5) sẽ được khởi tạo (8,5, 12,8, 12,1) với đối số tương ứng của nó. [Note: khởi tạo như thế được indeterminately trình tự liên quan đến nhau (1.9) với - cuối note]

Vì vậy, có, khởi tạo của các đối số được indeterminately trình tự liên quan đến nhau với. Các khởi tạo có thể xảy ra ở một trong những đơn đặt hàng:

std::string message = make_what_string(id); 
std::string id = std::move(id); 

std::string id = std::move(id); 
std::string message = make_what_string(id); 

Trong trường hợp thứ hai, make_what_string kết thúc lên làm việc với một chuyển-từ chuỗi.

Vì vậy, mặc dù std::move không thực sự di chuyển bất cứ điều gì, điều quan trọng là việc di chuyển thực tế cũng không bị liên quan đến đối số khác.

Định nghĩa của các nhà xây dựng di chuyển của basic_string(basic_string&& str) trạng thái:

[...] str còn lại trong tình trạng hợp lệ với giá trị không xác định.

Vì vậy, bạn không có hành vi không xác định, bạn có hành vi không xác định.

+0

@David: có, bởi vì di chuyển từ chuỗi có thể không còn hợp lệ, tùy thuộc vào việc thực hiện – Tom

+0

@Tom Trên thực tế, nó không xác định, không được xác định. :) –

+0

tôi nghĩ rằng đối số chuẩn có thể là: hoặc các tham số được khởi tạo trước hoặc sau khi gọi hàm. sau khi gọi hàm là không thể. do đó nó phải xảy ra trước đó. Và khởi tạo tham số được định nghĩa chuẩn để xảy ra trong ngữ cảnh của người gọi (vì vậy nó không được đánh giá "trong phần thân của hàm được gọi"). Vì vậy, không có trình tự (và không có trình tự tuần tự) của việc khởi tạo tham số đối với các đánh giá đối số. –

7

Nó không thực sự hợp lệ. Thứ tự của đánh giá đối số chức năng không được chỉ định. Nói cách khác, bạn không biết liệu trình biên dịch sẽ chọn trình tự này:

tmp1 = make_what_string(id); 
tmp2 = std::move(id); 
basic_foo(tmp1, tmp2); 

hay này:

tmp1 = std::move(id); 
tmp2 = make_what_string(id); //id has already been moved from! 
basic_foo(tmp2, tmp1); 
+0

Bạn có chắc chắn không? Tôi đã cập nhật câu hỏi của mình để xác định rõ hơn những gì tôi đang yêu cầu. – Tom

+0

Nhưng bản thân 'std :: move' không thực sự di chuyển bất cứ thứ gì, nó chỉ chuyển thành tham chiếu rvalue. – interjay

+0

@interjay: Thật vậy; nhưng (như câu hỏi mô tả) tạo thành giá trị của đối số hàm từ kết quả của 'std :: move' * does * di chuyển nó. –