2014-10-23 16 views
5

Tôi đang học C++ 11 và tôi có một câu hỏi liên quan đến di chuyển ngữ nghĩa và tham khảo rvalue. mẫu mã của tôi là như sau (C++ Shell URL là cpp.sh/8gt):Tại sao std :: di chuyển là cần thiết để gọi di chuyển gán nhà điều hành của std :: vector

#include <iostream> 
#include <vector> 

void aaa(std::vector<int>&& a) 
{ 
    std::cout << "size of a before move: " << a.size() << std::endl; 
    std::vector<int> v; 
    v = a; /*std::move(a)*/ 
    std::cout << "size of a after move: " << a.size() << std::endl; 
} 

int main() 
{ 
    std::vector<int> foo (3,0); 

    aaa(std::move(foo)); 

    return 0; 
} 

Kết quả của việc này là:

size of a before move: 3 
size of a after move: 3 

Có vẻ như các nhà điều hành di chuyển assign của std :: vector không được gọi tại dòng v = a trong hàm aaa, nếu không a sẽ có kích thước 0 thay vì 3.
Tuy nhiên nếu tôi thay đổi v = a-v = std::move(a) đầu ra trở thành

kích thước của một trước khi di chuyển: 3
kích thước của một sau khi di chuyển: 0

và tôi thinke các nhà điều hành di chuyển assign của std :: vector đã được viện dẫn này thời gian.

Quesiton của tôi là lý do tại sao toán tử gán không được gọi lần đầu tiên? Theo tham chiếu C++ std :: vector có một toán tử gán có tham chiếu rvalue.

bản sao (1) vector & operator = (const vector & x);
di chuyển (2) vectơ & nhà điều hành = (vector & & x);
danh sách bộ khởi tạo (3) vector & toán tử = (initializer_list il);

Sau đó, tại dòng v = a, vì được khai báo là tham chiếu rvalue, toán tử di chuyển sẽ được gọi. Tại sao chúng ta vẫn cần bọc một std :: move?

Rất cám ơn trước!

[sửa] Tôi nghĩ cả Loopunroller và SB Kerrek đều trả lời câu hỏi của tôi. Cảm ơn! Tôi không nghĩ rằng tôi có thể chọn hai câu trả lời vì vậy tôi sẽ chỉ chọn câu trả lời đầu tiên.

+2

Biểu thức đề cập đến tham chiếu rvalue * biến * là một giá trị lvalue, vì bạn có thể tham khảo biến nhiều lần. – dyp

+2

Bởi vì "tham chiếu rvalue" là một tên xấu và khó hiểu. Và bởi vì C++ là xấu và khó hiểu. –

+1

Đây là, tất nhiên, một điều tốt (một cách xấu và khó hiểu). Nếu không, 'v = a' sẽ âm thầm sửa đổi' a', gây ra chính xác kiểu 'weird_ptr'-style weirdness mà di chuyển ngữ nghĩa được dự định để tránh. –

Trả lời

9

Lưu ý này từ [expr]/6 có thể làm rõ những gì đang xảy ra (tôi nhấn mạnh):

[Lưu ý: Một biểu thức là một Xvalue nếu nó là:

  • sự kết quả của việc gọi hàm, cho dù ngầm hay rõ ràng, có kiểu trả về là tham chiếu rvalue đối với loại đối tượng,
  • một phép đúc tham chiếu rvalue cho loại đối tượng,
  • một biểu thức truy cập thành viên lớp chỉ định một thành viên không tĩnh dữ liệu của loại phi tài liệu tham khảo, trong đó biểu hiện đối tượng là một Xvalue , hoặc
  • một biểu .* con trỏ-to-thành viên, trong đó các toán hạng đầu tiên là một Xvalue và toán hạng thứ hai là một con trỏ tới thành viên dữ liệu.

Nói chung, tác động của quy tắc này là tài liệu tham khảo rvalue tên được coi là lvalues ​​ và tài liệu tham khảo rvalue giấu tên cho các đối tượng được coi là xvalues; tham chiếu rvalue cho các hàm được coi là giá trị cho dù được đặt tên hay không. - cuối note]

Nó không phải là khó để thấy rằng sự biểu hiện std::move(a) là một Xvalue theo danh sách trên (đạn một).
a là tên của một tham chiếu rvalue, và do đó một lvalue như là một biểu thức.

6

Biểu thức a là một giá trị. Biểu thức std::move(a) là một giá trị. Chỉ các giá trị gắn kết với các tham chiếu rvalue, tạo nên hàm khởi tạo di chuyển và di chuyển toán tử gán.

Cần lặp lại điều này cho chính mình cho đến khi nó có ý nghĩa: Đánh giá bất kỳ biến tham chiếu nào và cũng có thể bỏ qua bất kỳ con trỏ nào có thể bỏ qua, tạo ra một giá trị.

+2

Một điểm quan trọng là tại sao quy tắc này là như vậy. Khi anh ta gọi 'a = v', trình biên dịch không có cách nào để biết rằng anh ta sẽ không cần' v' nữa (và thực tế, anh ta sử dụng nó sau), vì vậy nó không thể thay đổi giá trị trừ khi bạn cho phép nó đến. –

+0

@JamesKanze: Đúng vậy. Như một quy tắc vàng, giá trị phải đảm bảo không được đặt bí danh. Điều này không được thi hành hoặc có thể thi hành ở cấp độ ngôn ngữ (các ngôn ngữ khác như Rust thực thi điều này), nhưng nó phải là cách người ta nghĩ về giá trị. –

+0

Một cách khác để ghi nhớ điều này là tham chiếu * rvalue * đang nói về những gì có thể * liên kết * với tham chiếu: giá trị rvalue. Nó không có nghĩa là sử dụng tham chiếu cũng giống như sử dụng một giá trị. –

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