Câu hỏi Rvalues, lvalues, xvalues, glvalues ​​và prvalues ​​là gì?


Trong C ++ 03, một biểu thức là một rvalue hoặc một lvalue.

Trong C ++ 11, một biểu thức có thể là:

  1. rvalue
  2. lvalue
  3. xvalue
  4. glvalue
  5. prvalue

Hai loại đã trở thành năm loại.

  • Các loại biểu thức mới này là gì?
  • Các danh mục mới này có liên quan như thế nào đến các danh mục rvalue và lvalue hiện có?
  • Các danh mục rvalue và lvalue có trong C ++ 0x giống như trong C ++ 03 không?
  • Tại sao các danh mục mới này lại cần? Là WG21 các vị thần chỉ cố gây nhầm lẫn cho chúng ta là những người chết?

1108
2017-08-30 15:02


gốc


@Philip Potter: Trong C ++ 03? Vâng. Một giá trị có thể được sử dụng như một giá trị vì có một chuyển đổi lvalue-to-rvalue tiêu chuẩn. - James McNellis
@ Tyler: "Nếu bạn có thể gán cho nó, nó là một giá trị, nếu không, đó là một rvalue." -> Sai, bạn có thể gán cho các giá trị của lớp: string("hello") = string("world"). - fredoverflow
Lưu ý rằng đây là danh mục giá trị. Có nhiều thuộc tính mà các biểu thức có thể có. Bao gồm các bit-field (đúng sai), tạm thời (đúng / sai) và kiểu (loại của nó). - Johannes Schaub - litb
Tôi nghĩ rằng liên kết của Fred ở trên là tốt hơn so với bất kỳ câu trả lời ở đây. Tuy nhiên, liên kết đã chết. Nó đã được chuyển đến: stroustrup.com/terminology.pdf - R. Martinho Fernandes
trong C ++ ngay cả các loại của bạn có các loại - nielsbot


Các câu trả lời:


Tôi đoán tài liệu này có thể phục vụ như là một giới thiệu không ngắn như vậy: n3055

Toàn bộ vụ thảm sát bắt đầu với ngữ nghĩa di chuyển. Một khi chúng ta có các biểu thức có thể được di chuyển và không được sao chép, đột nhiên dễ nắm bắt các quy tắc đòi hỏi sự phân biệt giữa các biểu thức có thể được di chuyển và theo hướng nào.

Từ những gì tôi đoán dựa trên dự thảo, sự khác biệt về giá trị r / l vẫn giữ nguyên, chỉ trong bối cảnh di chuyển mọi thứ trở nên lộn xộn.

Họ có cần thiết không? Có lẽ không phải nếu chúng ta muốn mất các tính năng mới. Nhưng để cho phép tối ưu hóa tốt hơn, chúng ta có lẽ nên ôm lấy chúng.

Báo giá n3055:

  • An lvalue (được gọi là, trong lịch sử, vì giá trị có thể xuất hiện trên phía bên tay trái của bài tập biểu thức) chỉ định một hàm hoặc một đối tượng. [Ví dụ: Nếu E là một biểu hiện của loại con trỏ, sau đó *E là một biểu thức lvalue đề cập đến đối tượng hoặc chức năng mà E điểm. Một ví dụ khác, kết quả của việc gọi một hàm có kiểu trả về là tham chiếu lvalue một lvalue.] 
  • An xvalue (một Giá trị "eXpiring") cũng đề cập đến đối tượng, thường gần cuối của nó suốt đời (để tài nguyên của nó có thể được di chuyển, ví dụ). Xvalue là kết quả của một số loại biểu thức liên quan đến rvalue tài liệu tham khảo. [Ví dụ: The kết quả của việc gọi một hàm có kiểu trả về là tham chiếu rvalue là một xvalue.]
  • A glvalue   ("Tổng quát" lvalue) là một lvalue hoặc một xvalue.
  • An rvalue (cái gọi là, trong lịch sử, bởi vì giá trị có thể xuất hiện ở phía bên tay phải của một biểu thức gán) là xvalue, một vật thể tạm thời hoặc subobject của nó, hoặc một giá trị đó là không liên kết với một đối tượng.
  • A prvalue (Rvalue "thuần túy") là một giá trị đó không phải là một xvalue. [Ví dụ: The kết quả của việc gọi một hàm có loại trả về không phải là tham chiếu là prvalue]

Tài liệu được đề cập là một tham chiếu tuyệt vời cho câu hỏi này, bởi vì nó cho thấy những thay đổi chính xác trong tiêu chuẩn đã xảy ra như là kết quả của việc giới thiệu danh pháp mới.


531
2017-08-30 15:09



Cảm ơn, câu trả lời này thực sự hữu ích! Nhưng trình biên dịch của tôi không đồng ý với các ví dụ của bạn về xvalues ​​và prvalues; chúng hoàn toàn ngược lại. Trả về bằng tham chiếu rvalue cho tôi một giá trị, và trả về bởi giá trị cho tôi một xvalue. Bạn đã làm cho họ bị lẫn lộn, hoặc là giường thử nghiệm của tôi bị hỏng? Tôi đã thử điều này với GCC 4.6.1, clang (từ svn) và MSVC, và tất cả đều hiển thị cùng một hành vi. - Kim Gräsman
Rất tiếc, tôi đã theo dõi liên kết và nhận thấy rằng các ví dụ có trong nguồn. Tôi sẽ tìm bản sao của tiêu chuẩn và kiểm tra những gì nó nói ... - Kim Gräsman
Tôi sử dụng các macro từ đây để kiểm tra các biểu thức khác nhau: stackoverflow.com/a/6114546/96963  Nó có thể là họ misdiagnose điều. - Kim Gräsman
Việc thêm xvalue không dành cho ngữ nghĩa di chuyển. Chỉ với cả lvalue và rvalue, ngữ nghĩa di chuyển, tham chiếu về phía trước và rvalue hoàn hảo vẫn hoạt động tốt. Tôi nghĩ rằng xvalue chỉ dành cho toán tử decltype: nếu biểu thức toán hạng là xvalue, thì decltype đưa ra kiểu tham chiếu rvalue. - ligand
@MuhamedCicak "Mỗi biểu thức là một giá trị hoặc một giá trị": đó là sự thật; và tiêu chuẩn (hoặc tài liệu n3055) không nói nó sai. Lý do câu này bị gạch chéo là bạn đang xem xét các thay đổi giữa hai phiên bản của tài liệu. Câu này đã bị xóa vì nó trở nên thừa sau khi giải thích chính xác hơn đã được thêm vào. - max


Các loại biểu thức mới này là gì?

Các FCD (n3092) có mô tả tuyệt vời:

- Một giá trị (được gọi là, trong lịch sử, bởi vì giá trị có thể xuất hiện trên   phía bên tay trái của bài tập   biểu thức) chỉ định một hàm hoặc   một đối tượng. [Ví dụ: Nếu E là một   biểu hiện của loại con trỏ, sau đó   * E là một biểu thức giá trị đề cập đến đối tượng hoặc hàm mà E   điểm. Một ví dụ khác, kết quả   gọi một hàm có trả về   loại là một tham chiếu lvalue là một   lvalue. —Về ví dụ]

- Một xvalue (một   Giá trị "eXpiring") cũng đề cập đến   đối tượng, thường gần cuối của nó   suốt đời (để tài nguyên của nó có thể   đã di chuyển, ví dụ). Một xvalue là   kết quả của một số loại biểu thức nhất định   liên quan đến tham chiếu rvalue (8.3.2). [   Ví dụ: Kết quả gọi điện   hàm có kiểu trả về là một   Tham chiếu rvalue là một xvalue. -kết thúc   thí dụ ]

- Một glvalue ("tổng quát"   lvalue) là một lvalue hoặc một xvalue.

- -   Một rvalue (được gọi là, lịch sử,   bởi vì giá trị có thể xuất hiện trên   phía bên tay phải của một bài tập   biểu thức) là một xvalue, tạm thời   đối tượng (12.2) hoặc subobject của nó, hoặc   một giá trị không được liên kết với   vật.

- Giá trị (giá trị "thuần túy") là   một giá trị không phải là một xvalue. [   Ví dụ: Kết quả gọi điện   hàm có kiểu trả về không phải là   tham chiếu là một giá trị. Giá trị của một   theo nghĩa đen như 12, 7.3e5 hoặc đúng là   cũng là một giá trị. —Về ví dụ]

Mỗi   biểu thức thuộc về chính xác một trong   các phân loại cơ bản trong   phân loại này: lvalue, xvalue, hoặc   prvalue. Thuộc tính này của một   biểu thức được gọi là giá trị của nó   thể loại. [Lưu ý: Thảo luận về   mỗi toán tử cài sẵn trong khoản 5   cho biết danh mục của giá trị   sản lượng và các loại giá trị của   operands nó mong đợi. Ví dụ:   các toán tử gán sẵn   rằng toán hạng bên trái là một lvalue và   rằng toán hạng bên phải là một giá trị   và mang lại một lvalue như là kết quả.   Các toán tử do người dùng định nghĩa là các hàm,   và các loại giá trị họ   kỳ vọng và lợi nhuận được xác định bởi   tham số và kiểu trả về của chúng. -kết thúc   chú thích

Tôi đề nghị bạn đọc toàn bộ phần 3.10 Giá trị và giá trị Tuy nhiên.

Các danh mục mới này có liên quan như thế nào đến các danh mục rvalue và lvalue hiện có?

Lần nữa:

Taxonomy

Các danh mục rvalue và lvalue có trong C ++ 0x giống như trong C ++ 03 không?

Các ngữ nghĩa của rvalues ​​đã phát triển đặc biệt với sự ra đời của ngữ nghĩa di chuyển.

Tại sao các danh mục mới này lại cần?

Vì vậy, việc xây dựng / chuyển nhượng có thể được xác định và hỗ trợ.


304
2017-08-31 08:08



Tôi thích sơ đồ ở đây. Tôi nghĩ việc bắt đầu câu trả lời có thể hữu ích "Mỗi biểu thức thuộc về chính xác một trong các phân loại cơ bản trong phân loại này: lvalue, xvalue, hoặc prvalue."  Sau đó, dễ dàng sử dụng sơ đồ để hiển thị ba lớp cơ bản đó được kết hợp để tạo ra glvalue và rvalue. - Aaron McDaid


Tôi sẽ bắt đầu với câu hỏi cuối cùng của bạn:

Tại sao các danh mục mới này lại cần?

Tiêu chuẩn C ++ chứa nhiều quy tắc xử lý danh mục giá trị của một biểu thức. Một số quy tắc tạo ra sự khác biệt giữa giá trị và rvalue. Ví dụ, khi nói đến độ phân giải quá tải. Các quy tắc khác tạo sự khác biệt giữa glvalue và prvalue. Ví dụ, bạn có thể có một glvalue với một loại không đầy đủ hoặc trừu tượng nhưng không có giá trị với một loại không đầy đủ hoặc trừu tượng. Trước khi chúng tôi có thuật ngữ này, các quy tắc thực sự cần phân biệt giữa glvalue / prvalue được gọi là lvalue / rvalue và chúng vô tình sai hoặc chứa nhiều giải thích và ngoại lệ đối với quy tắc a la "... trừ khi rvalue là do chưa được đặt tên tham khảo rvalue ... ". Vì vậy, nó có vẻ như là một ý tưởng tốt để chỉ cung cấp cho các khái niệm về glvalues ​​và prvalues ​​tên riêng của họ.

Các loại biểu thức mới này là gì?   Các danh mục mới này có liên quan như thế nào đến các danh mục rvalue và lvalue hiện có?

Chúng tôi vẫn có các thuật ngữ lvalue và rvalue tương thích với C ++ 98. Chúng tôi chỉ chia các giá trị thành hai phân nhóm, xvalues ​​và prvalues, và chúng tôi đề cập đến giá trị và xvalues ​​như glvalues. Xvalues ​​là một loại giá trị mới cho các tham chiếu rvalue chưa đặt tên. Mỗi biểu thức là một trong ba biểu thức: lvalue, xvalue, prvalue. Một sơ đồ Venn sẽ trông như thế này:

    ______ ______
   /      X      \
  /      / \      \
 |   l  | x |  pr  |
  \      \ /      /
   \______X______/
       gl    r

Ví dụ với hàm:

int   prvalue();
int&  lvalue();
int&& xvalue();

Nhưng cũng đừng quên rằng tham chiếu rvalue được đặt tên là lvalues:

void foo(int&& t) {
  // t is initialized with an rvalue expression
  // but is actually an lvalue expression itself
}

159
2018-03-04 06:32





Tại sao các danh mục mới này lại cần? Có phải các vị thần WG21 chỉ đang cố làm cho chúng ta nhầm lẫn với chúng ta?

Tôi không cảm thấy rằng các câu trả lời khác (tốt mặc dù nhiều người trong số họ) thực sự nắm bắt được câu trả lời cho câu hỏi cụ thể này. Có, các thể loại này và tồn tại như vậy để cho phép các ngữ nghĩa di chuyển, nhưng sự phức tạp tồn tại vì một lý do. Đây là nguyên tắc bất biến của việc di chuyển nội dung trong C ++ 11:

Bạn phải di chuyển chỉ khi nó là không thể nghi ngờ an toàn để làm như vậy.

Đó là lý do tại sao các loại này tồn tại: để có thể nói về các giá trị mà nó an toàn để di chuyển từ chúng, và để nói về các giá trị mà nó không phải là.

Trong phiên bản sớm nhất của tài liệu tham khảo giá trị r, sự di chuyển diễn ra dễ dàng. Quá dễ dàng. Dễ dàng đủ để có nhiều tiềm năng cho việc di chuyển mọi thứ khi người dùng không thực sự có ý nghĩa.

Dưới đây là những trường hợp theo đó an toàn để di chuyển một cái gì đó:

  1. Khi đó là tạm thời hoặc subobject của nó. (prvalue)
  2. Khi người dùng có nói rõ ràng để di chuyển nó.

Nếu bạn làm điều này:

SomeType &&Func() { ... }

SomeType &&val = Func();
SomeType otherVal{val};

Điều này làm gì? Trong các phiên bản cũ hơn của spec, trước khi 5 giá trị được đưa vào, điều này sẽ kích động một động thái. Tất nhiên nó. Bạn đã thông qua một tham chiếu rvalue cho hàm tạo, và do đó nó liên kết với hàm tạo có tham chiếu rvalue. Đó là hiển nhiên.

Chỉ có một vấn đề với điều này; bạn đã không hỏi di chuyển thôi. Ồ, bạn có thể nói rằng && nên có được một đầu mối, nhưng điều đó không thay đổi thực tế là nó đã phá vỡ quy tắc. val không phải là tạm thời vì thời gian không có tên. Bạn có thể đã kéo dài tuổi thọ của tạm thời, nhưng điều đó có nghĩa là nó không phải là tạm thời; nó giống như bất kỳ biến ngăn xếp nào khác.

Nếu nó không phải là tạm thời, và bạn không yêu cầu di chuyển nó, thì di chuyển là sai rồi.

Các giải pháp rõ ràng là để làm cho val một lvalue. Điều này có nghĩa là bạn không thể di chuyển từ nó. OK, tốt; nó được đặt tên, vì vậy nó là một lvalue.

Một khi bạn làm điều đó, bạn không còn có thể nói rằng SomeType&& có nghĩa là cùng một điều ở khắp mọi nơi. Bây giờ bạn đã tạo sự khác biệt giữa các tham chiếu rvalue có tên và tham chiếu rvalue chưa đặt tên. Các tham chiếu rvalue có tên là lvalues; đó là giải pháp của chúng tôi ở trên. Vì vậy, chúng ta gọi các tham chiếu rvalue chưa được đặt tên là gì (giá trị trả về từ Func ở trên)?

Nó không phải là một lvalue, bởi vì bạn không thể di chuyển từ một lvalue. Và chúng ta nhu cầu để có thể di chuyển bằng cách trả lại &&; làm thế nào khác bạn có thể nói một cách rõ ràng để di chuyển một cái gì đó? Đó là cái gì std::movetrả về, sau khi tất cả. Nó không phải là một rvalue (kiểu cũ), bởi vì nó có thể ở bên trái của một phương trình (mọi thứ thực sự phức tạp hơn một chút, xem câu hỏi này và các bình luận bên dưới). Nó không phải là một giá trị cũng không phải là một rvalue; đó là một loại điều mới.

Những gì chúng tôi có là một giá trị mà bạn có thể coi là một giá trị, ngoại trừ rằng nó là hoàn toàn di chuyển từ. Chúng tôi gọi nó là một xvalue.

Lưu ý rằng xvalues ​​là những gì làm cho chúng ta có được hai loại giá trị khác:

  • Giá trị thực sự chỉ là tên mới cho loại giá trị trước đó, tức là chúng là giá trị mà không phải xvalues.

  • Glvalues ​​là sự kết hợp của xvalues ​​và lvalues ​​trong một nhóm, bởi vì chúng chia sẻ rất nhiều thuộc tính chung.

Vì vậy, thực sự, tất cả đi xuống để xvalues ​​và sự cần thiết để hạn chế chuyển động đến chính xác và chỉ những nơi nhất định. Những địa điểm này được xác định bởi danh mục rvalue; prvalues ​​là các động thái tiềm ẩn, và xvalues ​​là các động thái rõ ràng (std::move trả về một xvalue).


132
2017-07-03 12:30



Điều này là thú vị nhưng nó biên dịch? Không nên Func có một tuyên bố trở lại? - ThomasMcLeod
@ Thomas: Đó là một ví dụ; không quan trọng nó tạo ra giá trị trả về như thế nào. Điều quan trọng là nó trả về một &&. - Nicol Bolas
Lưu ý: các giá trị có thể ở phía bên trái của một phương trình, cũng như - như trong X foo(); foo() = X; ... Vì lý do cơ bản này, tôi không thể thực hiện theo câu trả lời tuyệt vời ở trên cho đến cuối cùng, bởi vì bạn thực sự chỉ phân biệt giữa xvalue mới và giá trị theo kiểu cũ, dựa trên thực tế là nó có thể trên lhs. - Dan Nissenbaum
X là một lớp; X foo(); là một tuyên bố chức năng, và foo() = X(); là một dòng mã. (Tôi đã rời khỏi bộ ngoặc đơn thứ hai trong foo() = X(); trong bình luận ở trên của tôi.) Đối với câu hỏi tôi vừa đăng với việc sử dụng này được đánh dấu, hãy xem stackoverflow.com/questions/15482508/… - Dan Nissenbaum
@DanNissenbaum "xvalue không thể ở phía bên trái của biểu thức gán" - tại sao không? Xem ideone.com/wyrxiT - Mikhail


IMHO, giải thích tốt nhất về ý nghĩa của nó đã cho chúng tôi Stroustrup + xem xét các ví dụ về Dániel Sándor và Mohan:

Stroustrup:

Bây giờ tôi đã rất lo lắng. Rõ ràng chúng tôi đã đi đến một sự bế tắc hoặc   một mớ hỗn độn hoặc cả hai. Tôi đã dành giờ ăn trưa để phân tích xem   của các thuộc tính (của các giá trị) là độc lập. Chỉ có hai   các thuộc tính độc lập:

  • has identity - tức là và địa chỉ, một con trỏ, người dùng có thể xác định xem hai bản sao có giống nhau hay không, v.v.
  • can be moved from - tức là chúng tôi được phép rời khỏi nguồn của "bản sao" ở một số trạng thái không xác định, nhưng hợp lệ

Điều này dẫn tôi đến kết luận rằng có chính xác ba loại   giá trị (sử dụng thủ thuật ký hiệu regex bằng cách sử dụng một chữ cái viết hoa để   cho thấy một tiêu cực - tôi đã vội vàng):

  • iM: có danh tính và không thể di chuyển từ
  • im: có danh tính và có thể được di chuyển từ (ví dụ: kết quả của việc đúc một giá trị lvalue sang tham chiếu rvalue)
  • Im: không có danh tính và có thể được chuyển từ Khả năng thứ tư (IM: không có danh tính và không thể di chuyển được) không   hữu ích trong C++ (hoặc, tôi nghĩ) bằng bất kỳ ngôn ngữ nào khác.

Ngoài ba loại giá trị cơ bản này, chúng tôi   có hai khái quát rõ ràng tương ứng với hai   các thuộc tính độc lập:

  • i: có bản sắc
  • m: có thể được chuyển từ

Điều này đã khiến tôi đưa biểu đồ này lên bảng:    enter image description here

Đặt tên

Tôi quan sát thấy rằng chúng tôi chỉ có quyền tự do hạn chế để đặt tên: Hai điểm để   bên trái (có nhãn iM và i) là những người có nhiều hay ít   hình thức đã được gọi lvalues và hai điểm ở bên phải   (có nhãn m và Im) là những người có hình thức nhiều hơn hoặc ít hơn   đã gọi rvalues. Điều này phải được phản ánh trong việc đặt tên của chúng tôi. Đó là,   "chân" bên trái của W nên có tên liên quan đến lvalue và   đúng "chân" của W nên có tên liên quan đến rvalue. tôi ghi chú   toàn bộ thảo luận / vấn đề này nảy sinh từ sự ra đời của   tham khảo rvalue và di chuyển ngữ nghĩa. Những khái niệm này không tồn tại   trong thế giới của Strachey chỉ bao gồm rvalues và lvalues. Người nào   quan sát thấy rằng những ý tưởng

  • Mỗi value là một trong hai lvalue hoặc một rvalue
  • An lvalue không phải là rvalue và một rvalue không phải là lvalue 

được nhúng sâu vào ý thức của chúng tôi, các thuộc tính rất hữu ích và   dấu vết của sự phân đôi này có thể được tìm thấy trên tất cả các tiêu chuẩn dự thảo. Chúng tôi   tất cả đều đồng ý rằng chúng ta nên bảo tồn những tài sản đó (và làm cho chúng   tóm lược). Điều này tiếp tục hạn chế lựa chọn đặt tên của chúng tôi. Tôi quan sát thấy rằng   sử dụng từ ngữ thư viện chuẩn rvalue có nghĩa là m (các   tổng quát), để duy trì kỳ vọng và văn bản của   thư viện chuẩn ở phía dưới bên phải của W nên được đặt tên    rvalue.

Điều này dẫn đến một cuộc thảo luận tập trung về đặt tên. Đầu tiên, chúng tôi cần quyết định   trên lvalue. Nên lvalue nghĩa là iM hoặc tổng quát hóa i? Led   bởi Doug Gregor, chúng tôi liệt kê các địa điểm trong từ ngữ ngôn ngữ cốt lõi   nơi từ lvalue đủ điều kiện để có nghĩa là cái này hay cái kia. A   danh sách đã được thực hiện và trong hầu hết các trường hợp và trong văn bản khó khăn / giòn nhất    lvalue hiện có nghĩa là iM. Đây là ý nghĩa cổ điển của lvalue   bởi vì "trong những ngày xưa" không có gì được di chuyển; move là một khái niệm mới lạ   trong C++0x. Ngoài ra, đặt tên cho điểm phức tạp của W  lvalue cho chúng ta   tài sản mà mọi giá trị là một lvalue hoặc một rvalue, nhưng không phải cả hai.

Vì vậy, điểm trên cùng bên trái của W Là lvalue và điểm dưới cùng bên phải   Là rvalue. Điều gì làm cho các điểm phía dưới bên trái và trên cùng bên phải?   Điểm dưới cùng bên trái là sự tổng quát hóa của giá trị cổ điển,   cho phép di chuyển. Vì vậy, nó là một generalized lvalue. Chúng tôi đặt tên cho nó    glvalue. Bạn có thể giải thích về chữ viết tắt, nhưng (tôi nghĩ) không   với logic. Chúng tôi giả định rằng việc sử dụng nghiêm túc generalized lvalue   bằng cách nào đó sẽ được viết tắt, vì vậy chúng tôi đã làm tốt hơn   ngay lập tức (hoặc nhầm lẫn rủi ro). Điểm trên cùng bên phải của W ít hơn   nói chung so với góc dưới bên phải (bây giờ, bao giờ hết, được gọi là rvalue). Cái đó   điểm đại diện cho khái niệm thuần túy ban đầu của một đối tượng bạn có thể di chuyển   từ bởi vì nó không thể được nhắc đến một lần nữa (ngoại trừ bởi một destructor).   Tôi thích cụm từ specialized rvalue trái ngược với generalized lvalue nhưng pure rvalue viết tắt thành prvalue thắng (và   có lẽ đúng vậy). Vì vậy, chân trái của W là lvalue và    glvalue và chân phải là prvalue và rvalue. Ngẫu nhiên,   mọi giá trị đều là glvalue hoặc prvalue, nhưng không phải cả hai.

Điều này rời khỏi phần trên cùng của W: im; đó là, các giá trị có   danh tính và có thể được di chuyển. Chúng tôi thực sự không có bất kỳ hướng dẫn nào   chúng tôi là một cái tên tốt cho những con thú bí truyền này. Chúng quan trọng đối với   những người làm việc với văn bản chuẩn (dự thảo) nhưng không có khả năng   trở thành một tên hộ gia đình. Chúng tôi không tìm thấy bất kỳ ràng buộc thực sự nào trên   đặt tên để hướng dẫn chúng tôi, vì vậy chúng tôi đã chọn ‘x’ cho trung tâm, không xác định,   lạ, chỉ xpert, hoặc thậm chí là x-rated.

Steve showing off the final product


94
2018-01-20 13:46



vâng, tốt hơn là đọc các đề xuất và thảo luận ban đầu của comitee C ++, so với tiêu chuẩn, nếu bạn muốn hiểu ý nghĩa của chúng: D - Ivan Kush
Literals không có bản sắc và không thể chuyển từ; chúng vẫn hữu ích. - DrPizza
Tôi chỉ muốn làm rõ một điều. int && f () {return 1; } và MyClass && g () {return MyClass (); } trả về xvalue, phải không? Sau đó, nơi tôi có thể tìm thấy danh tính của các biểu thức f (); Và g();"? Họ có danh tính, bởi vì có một biểu hiện khác trong câu lệnh trả về, nó đề cập đến cùng một đối tượng khi họ nói đến - tôi có hiểu nó đúng không? - Dániel Sándor
@DrPizza Theo tiêu chuẩn: chuỗi ký tự là lvalues, tất cả các chữ khác là prvalueS. Nói đúng ra, bạn có thể đưa ra một lý lẽ để nói rằng các chuỗi ký tự không phải chuỗi sẽ là bất động, nhưng đó không phải là cách viết chuẩn. - Brian Vandenberg


GIỚI THIỆU

ISOC ++ 11 (chính thức ISO / IEC 14882: 2011) là phiên bản mới nhất của tiêu chuẩn ngôn ngữ lập trình C ++. Nó chứa một số tính năng mới, và các khái niệm, ví dụ:

  • tham chiếu rvalue
  • xvalue, glvalue, danh mục giá trị biểu thức giá trị
  • di chuyển ngữ nghĩa

Nếu chúng ta muốn hiểu các khái niệm về các loại giá trị biểu thức mới, chúng ta phải biết rằng có các tham chiếu rvalue và lvalue. Nó là tốt hơn để biết rvalues ​​có thể được chuyển đến tham chiếu rvalue không const.

int& r_i=7; // compile error
int&& rr_i=7; // OK

Chúng ta có thể đạt được một số trực giác của các khái niệm về các loại giá trị nếu chúng ta trích dẫn phần phụ có tên là Lvalues ​​và rvalues ​​từ bản thảo làm việc N3337 (bản nháp tương tự nhất với chuẩn ISOC ++ 11 được xuất bản).

3.10 Giá trị và giá trị [basic.lval]

1 Biểu thức được phân loại theo phân loại trong Hình 1.

  • Một giá trị (được gọi là, theo lịch sử, bởi vì các giá trị có thể xuất hiện ở phía bên trái của một biểu thức gán) chỉ định một hàm   hoặc một đối tượng. [Ví dụ: Nếu E là biểu thức của loại con trỏ, thì   * E là một biểu thức lvalue đề cập đến đối tượng hoặc chức năng mà điểm E. Một ví dụ khác, kết quả của việc gọi một hàm   có kiểu trả về là tham chiếu lvalue là một lvalue. —Về ví dụ]
  • Giá trị xvalue (giá trị "eXpiring") cũng đề cập đến một đối tượng, thường gần cuối vòng đời của nó (để tài nguyên của nó có thể được di chuyển, cho   thí dụ). Xvalue là kết quả của một số loại biểu thức nhất định   liên quan đến tham chiếu rvalue (8.3.2). [Ví dụ: Kết quả gọi điện   một hàm có kiểu trả về là tham chiếu rvalue là một xvalue. -kết thúc   thí dụ ]
  • Một glvalue ("tổng quát" lvalue) là một lvalue hoặc một xvalue.
  • Một giá trị (được gọi là, theo lịch sử, bởi vì các giá trị có thể xuất hiện ở phía bên phải của một biểu thức gán) là một xvalue, một
      đối tượng tạm thời (12.2) hoặc subobject của nó, hoặc một giá trị không phải là
      liên kết với một đối tượng.
  • Giá trị (giá trị "thuần túy") là một giá trị không phải là một xvalue. [Ví dụ: Kết quả của việc gọi một hàm có kiểu trả về không phải là
      tham chiếu là một giá trị. Giá trị của một chữ như 12, 7.3e5 hoặc
      đúng cũng là một giá trị. —Về ví dụ]

Mỗi biểu thức thuộc về chính xác một trong những điều cơ bản   phân loại trong phân loại này: lvalue, xvalue hoặc prvalue. Điều này   thuộc tính của một biểu thức được gọi là danh mục giá trị của nó.

Nhưng tôi không hoàn toàn chắc chắn rằng tiểu mục này là đủ để hiểu các khái niệm rõ ràng, bởi vì "thường" không thực sự chung chung, "gần cuối đời" không thực sự cụ thể, "liên quan đến tham chiếu rvalue" không thực sự rõ ràng, và "Ví dụ: Kết quả của việc gọi một hàm có kiểu trả về là tham chiếu rvalue là xvalue." có vẻ như rắn đang cắn đuôi nó.

CÁC LOẠI GIÁ TRỊ TỐI ƯU

Mỗi biểu thức thuộc về chính xác một danh mục giá trị chính. Các loại giá trị này là các loại giá trị lvalue, xvalue và prvalue.

lvalues

Biểu thức E thuộc về loại lvalue nếu và chỉ khi E đề cập đến một thực thể ALREADY đã có một danh tính (địa chỉ, tên hoặc bí danh) mà làm cho nó có thể truy cập bên ngoài của E.

#include <iostream>

int i=7;

const int& f(){
    return i;
}

int main()
{
    std::cout<<&"www"<<std::endl; // This address ...
    std::cout<<&"www"<<std::endl; // ... and this address are the same.
    "www"; // The expression "www" in this row is an lvalue expression, because it refers to the same entity ...
    "www"; // ... as the entity the expression "www" in this row refers to.

    i; // The expression i in this row is an lvalue expression, because it refers to the same entity ...
    i; // ... as the entity the expression i in this row refers to.

    int* p_i=new int(7);
    *p_i; // The expression *p_i in this row is an lvalue expression, because it refers to the same entity ...
    *p_i; // ... as the entity the expression *p_i in this row refers to.

    const int& r_I=7;
    r_I; // The expression r_I in this row is an lvalue expression, because it refers to the same entity ...
    r_I; // ... as the entity the expression r_I in this row refers to.

    f(); // The expression f() in this row is an lvalue expression, because it refers to the same entity ...
    i; // ... as the entity the expression f() in this row refers to.

    return 0;
}

xvalues

Biểu thức E thuộc thể loại xvalue nếu và chỉ khi nó là

- kết quả của việc gọi một hàm, cho dù ngầm hoặc rõ ràng, có kiểu trả về là tham chiếu rvalue cho kiểu đối tượng được trả về hoặc

int&& f(){
    return 3;
}

int main()
{
    f(); // The expression f() belongs to the xvalue category, because f() return type is an rvalue reference to object type.

    return 0;
}

- một dàn diễn viên tham chiếu rvalue cho loại đối tượng hoặc

int main()
{
    static_cast<int&&>(7); // The expression static_cast<int&&>(7) belongs to the xvalue category, because it is a cast to an rvalue reference to object type.
    std::move(7); // std::move(7) is equivalent to static_cast<int&&>(7).

    return 0;
}

- biểu thức truy cập thành viên lớp chỉ định thành viên dữ liệu không tĩnh của loại không tham chiếu trong đó biểu thức đối tượng là xvalue hoặc

struct As
{
    int i;
};

As&& f(){
    return As();
}

int main()
{
    f().i; // The expression f().i belongs to the xvalue category, because As::i is a non-static data member of non-reference type, and the subexpression f() belongs to the xvlaue category.

    return 0;
}

- một biểu thức con trỏ đến thành viên trong đó 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.

Lưu ý rằng hiệu ứng của các quy tắc trên là các tham chiếu rvalue được đặt tên cho các đối tượng được coi là các giá trị và các tham chiếu rvalue chưa được đặt 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.

#include <functional>

struct As
{
    int i;
};

As&& f(){
    return As();
}

int main()
{
    f(); // The expression f() belongs to the xvalue category, because it refers to an unnamed rvalue reference to object.
    As&& rr_a=As();
    rr_a; // The expression rr_a belongs to the lvalue category, because it refers to a named rvalue reference to object.
    std::ref(f); // The expression std::ref(f) belongs to the lvalue category, because it refers to an rvalue reference to function.

    return 0;
}

prvalues

Biểu thức E thuộc về danh mục giá trị nếu và chỉ khi E không thuộc về giá trị lvalue hay loại xvalue.

struct As
{
    void f(){
        this; // The expression this is a prvalue expression. Note, that the expression this is not a variable.
    }
};

As f(){
    return As();
}

int main()
{
    f(); // The expression f() belongs to the prvalue category, because it belongs neither to the lvalue nor to the xvalue category.

    return 0;
}

CÁC LOẠI GIÁ TRỊ GIÁ TRỊ

Có hai loại giá trị hỗn hợp quan trọng hơn nữa. Các loại giá trị này là các loại rvalue và glvalue.

rvalues

Biểu thức E thuộc về danh mục rvalue nếu và chỉ khi E thuộc thể loại xvalue, hoặc đến loại giá trị gia tăng.

Lưu ý rằng định nghĩa này có nghĩa là biểu thức E thuộc về danh mục rvalue nếu và chỉ khi E đề cập đến một thực thể không có bất kỳ nhận dạng nào khiến cho nó có thể truy cập được bên ngoài E YET.

glvalues

Biểu thức E thuộc về danh mục glvalue nếu và chỉ khi E thuộc về danh mục lvalue hoặc đến danh mục xvalue.

QUY TẮC THỰC TIỄN

Scott Meyer có được phát hành một quy tắc rất hữu ích của ngón tay cái để phân biệt rvalues ​​từ lvalues.

  • Nếu bạn có thể lấy địa chỉ của một biểu thức, biểu thức là một giá trị.
  • Nếu loại biểu thức là tham chiếu giá trị lvalue (ví dụ: T & hoặc const T &, v.v.), biểu thức đó là một giá trị.
  • Mặt khác, biểu thức là một giá trị. Về mặt khái niệm (và cũng thường trong thực tế), các giá trị tương ứng với các vật thể tạm thời, như vậy   như được trả lại từ các hàm hoặc được tạo thông qua loại ngầm   chuyển đổi. Hầu hết các giá trị bằng chữ (ví dụ: 10 và 5.3) cũng là các giá trị.

34
2017-08-30 16:46



Tôi vẫn không nhận được gvalue :(, Nó sẽ giúp nếu bạn đã cung cấp các ví dụ tuyệt vời như bạn đã làm cho các loại khác :) - Angelus Mortis
Tất cả các ví dụ về giá trị và tất cả các ví dụ cho xvalues ​​là ví dụ cho các giá trị. Cảm ơn bạn đã chỉnh sửa! - Dániel Sándor
Vậy tại sao gvalue lại được giới thiệu theo tiêu chuẩn mà tôi tự hỏi? Ý tôi là chỉ có lvalue, rvalue, xvalue và prvalue mới có đủ điều kiện. Cảm ơn một lần nữa :) - Angelus Mortis
Bạn đúng rồi. Ba loại giá trị chính được sufficed. Rvalue cũng không cần thiết. Tôi nghĩ rvalue và glvalue là tiêu chuẩn để thuận tiện. - Dániel Sándor
Đã có một thời gian khó hiểu struct As{void f(){this;}} các this biến là một giá trị. tôi đã nghĩ this nên là một lvalue. Cho đến tiêu chuẩn 9.3.2 cho biết: Trong phần thân của hàm thành viên không tĩnh (9.3), từ khóa này là biểu thức giá trị. - r0ng


Các thể loại của C ++ 03 quá hạn chế để nắm bắt sự giới thiệu của các tham chiếu rvalue một cách chính xác thành các thuộc tính biểu thức.

Với sự giới thiệu của họ, người ta nói rằng một tham chiếu rvalue không được đặt tên đánh giá một giá trị, như vậy độ phân giải quá tải sẽ thích ràng buộc tham chiếu rvalue, mà sẽ làm cho nó chọn các nhà thầu di chuyển trên các nhà xây dựng sao chép. Nhưng nó đã được tìm thấy rằng điều này gây ra vấn đề xung quanh, ví dụ với Các loại động và với bằng cấp.

Để hiển thị điều này, hãy cân nhắc

int const&& f();

int main() {
  int &&i = f(); // disgusting!
}

Trên các bản nháp trước xvalue, điều này được cho phép, bởi vì trong C ++ 03, các giá trị của các loại không phải lớp không bao giờ cv đủ điều kiện. Nhưng nó được dự định const áp dụng trong trường hợp tham chiếu rvalue, bởi vì ở đây chúng tôi làm tham chiếu đến các đối tượng (= bộ nhớ!), và thả const từ các giá trị không thuộc lớp là chủ yếu vì lý do không có đối tượng xung quanh.

Vấn đề cho các loại động có tính chất tương tự. Trong C ++ 03, các giá trị của kiểu lớp có một kiểu động đã biết - đó là kiểu tĩnh của biểu thức đó. Bởi vì để có nó một cách khác, bạn cần tài liệu tham khảo hoặc dereferences, mà đánh giá đến một lvalue. Điều đó không đúng với các tham chiếu rvalue chưa được đặt tên, nhưng chúng có thể cho thấy hành vi đa hình. Vì vậy, để giải quyết nó,

  • tham chiếu rvalue chưa đặt tên trở thành xvalues. Chúng có thể đủ điều kiện và có khả năng có loại động khác nhau. Họ làm, như dự định, thích tham chiếu rvalue trong quá tải, và sẽ không liên kết với các tham chiếu không phải lvalue.

  • Những gì trước đây là một rvalue (literals, các đối tượng được tạo bởi các phôi tới các kiểu không tham chiếu) bây giờ trở thành một prvalue. Họ có cùng sở thích với xvalues ​​trong quá tải.

  • Những gì trước đây là một lvalue vẫn là một lvalue.

Và hai nhóm được thực hiện để nắm bắt những nhóm có thể đủ điều kiện và có thể có các loại động khác nhau (glvalues) và những nơi quá tải thích ràng buộc tham chiếu rvalue (rvalues).


32
2018-06-17 02:20



câu trả lời rõ ràng là hợp lý. xvalue chỉ là rvalue có thể được đánh giá cv và năng động! - ligand