lỗi: khởi tạo không hợp lệ tham chiếu không phải const của kiểu 'int &' từ một giá trị của kiểu 'int'


87

Dạng sai:

int &z = 12;

Hình thức đúng:

int y;
int &r = y;

Câu hỏi :
Tại sao mã đầu tiên bị sai? " Ý nghĩa " của lỗi trong tiêu đề là gì?


4
Tạm thời không thể bị ràng buộc với các tham chiếu không cố định. int (12) là tạm thời trong trường hợp này.
Prasoon Saurav,

@PrasoonSaurav Theo bạn tạm thời là 12 nghĩa là gì? Thiếu khái niệm ở đây (về phía tôi :))
Aquarius_Girl

2
Lưu ý rằng không có lý do kỹ thuật nghiêm ngặt nào cho hạn chế này. Nó sẽ dễ dàng thực hiện như vậy để cho phép các tham chiếu có thể thay đổi thành các thời gian tạm thời. Cấm nó là một quyết định thiết kế của C ++, vì một cấu trúc như vậy sẽ là thiết kế kém với nguy cơ vô tình bị lạm dụng hơn nhiều so với tiện ích chính hãng. (Tôi chỉ một lần tìm thấy một nhu cầu giả tạo cho một điều như vậy.)
Kerrek SB

@KerrekSB nhu cầu phổ biến nhất cho một liên kết ref với đối tượng rvalue có lẽ là(ostringstream() << "x=" << x).str()
tò mò.

@curiousguy: Vâng, đó là nội dung của link tôi đã đăng.
Kerrek SB

Câu trả lời:


132

C ++ 03 3.10 / 1 nói: "Mọi biểu thức đều là một giá trị hoặc một giá trị." Điều quan trọng cần nhớ là lvalueness so với rvalueness là thuộc tính của biểu thức, không phải của đối tượng.

Giá trị đặt tên các đối tượng tồn tại ngoài một biểu thức. Ví dụ, obj, *ptr, ptr[index], và ++xtất cả đều lvalues.

Giá trị là giá trị tạm thời biến mất ở cuối biểu thức đầy đủ mà chúng tồn tại ("ở dấu chấm phẩy"). Ví dụ, 1729, x + y, std::string("meow"), và x++tất cả đều rvalues.

Toán tử address-of yêu cầu rằng "toán hạng của nó phải là một giá trị". nếu chúng ta 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 không nó là một giá trị.

 &obj; //  valid
 &12;  //invalid

2
" nếu chúng ta có thể lấy địa chỉ của một biểu thức, biểu thức là giá trị, nếu không thì đó là giá trị. " Giá như C ++ đơn giản như vậy! (nhưng sắc thái không thực sự liên quan ở đây) "Rvalues ​​are tạm thời" tạm thời cái gì? các đối tượng?
tò mò

2
@curiousguyRvalues ​​là các giá trị tạm thời biến mất ở cuối biểu thức đầy đủ mà chúng sống. Để trả lời đơn giản tại sao biểu thức int & = 12;không hợp lệ, tiêu chuẩn nói rằng một chuỗi ký tự là giá trị, các ký tự khác là giá trị.
BruceAdi

@curiousguy: Vâng. Giá trị là đối tượng tạm thời , nhưng không phải tất cả các giá trị đều là đối tượng tạm thời; một số thậm chí không phải là đối tượng.
Nawaz

" Ví dụ, năm 1729, x + y, std :: string (" meo meo "), và x ++ đều rvalues. " Nhưng std::string("meow")xây dựng một đối tượng kiểu std::stringvà mang lại một rvalue mà chỉ định đối tượng này, 1729không có tác dụng phụ và sản lượng giá trị 1729 như là một loại giá trị int.
tò mò

1
@curiousguy: Câu nói "Lvalues name objects that persist beyond a single expression."đúng 100%. Ngược lại, ví dụ của bạn (const int &)1là không chính xác, bởi vì Nó KHÔNG phải là một đối tượng "được đặt tên".
Nawaz

53
int &z = 12;

Ở phía bên phải, một đối tượng tạm thời của kiểu intđược tạo từ ký tự tích phân 12, nhưng tạm thời không thể bị ràng buộc với tham chiếu không phải const. Do đó lỗi. Nó giống như:

int &z = int(12); //still same error

Tại sao một tạm thời được tạo ra? Bởi vì một tham chiếu phải tham chiếu đến một đối tượng trong bộ nhớ, và để một đối tượng tồn tại, nó phải được tạo trước. Vì đối tượng không được đặt tên nên nó là đối tượng tạm thời . Nó không có tên. Từ lời giải thích này, nó đã trở nên khá rõ ràng tại sao trường hợp thứ hai lại tốt.

Một đối tượng tạm thời có thể được liên kết với tham chiếu const, có nghĩa là, bạn có thể làm điều này:

const int &z = 12; //ok

Tham chiếu C ++ 11 và Rvalue:

Vì lợi ích của sự đầy đủ, tôi muốn nói thêm rằng C ++ 11 đã giới thiệu tham chiếu rvalue, có thể liên kết với đối tượng tạm thời. Vì vậy, trong C ++ 11, bạn có thể viết như sau:

int && z = 12; //C+11 only 

Lưu ý rằng có &&intead of &. Cũng lưu ý rằng constkhông cần thiết nữa, mặc dù đối tượng zliên kết với là một đối tượng tạm thời được tạo ra từ tích phân-chữ 12.

Kể từ khi C ++ 11 đã giới thiệu tham chiếu rvalue , int&bây giờ được gọi là tham chiếu lvalue .


10

12là một hằng số thời gian biên dịch không thể thay đổi không giống như dữ liệu được tham chiếu bởi int&. Những gì bạn có thể làm là

const int& z = 12;

2
@curiousguy, hãy nhất quán, bạn đã nói "Bạn phải hiểu rằng đây là các quy tắc C ++. Chúng tồn tại và không cần bất kỳ lời biện minh nào" (chưa kể đến ấn bản đầu tiên). Làm thế nào để tôi giải thích sự phàn nàn của bạn về việc không cho đi những gì theo bản thân bạn là không cần thiết?
Michael Krelin - hacker

2
@ MichaelKrelin-hacker: Về mặt kỹ thuật thì không, bạn không thể (không bao giờ) ràng buộc một tham chiếu đến một giá trị (hoặc hằng số thời gian biên dịch), tiêu chuẩn khá rõ ràng về những gì thực sự xảy ra: Nếu không, tạm thời kiểu “cv1 T1” được tạo và được khởi tạo từ biểu thức trình khởi tạo bằng cách sử dụng các quy tắc cho khởi tạo sao chép không tham chiếu (8.5). Tham chiếu sau đó được liên kết với tạm thời Nghĩa là, cú pháp được cho phép, nhưng ngữ nghĩa không phải là những liên kết tham chiếu đến hằng số, mà là ràng buộc nó với tạm thời được tạo ra một cách ẩn ý.
David Rodríguez - dribeas

1
@curiousguy: Các quy tắc của ngôn ngữ là một phần của thiết kế, và thường là không, có những lý do giải thích tại sao ngôn ngữ được thiết kế như vậy. Trong trường hợp cụ thể này, như trong C, bạn không được phép lấy địa chỉ của một giá trị (nó không có), cũng như bạn không thể ràng buộc một tham chiếu. Bây giờ hãy xem xét một hàm void f( vector<int> const & ), nó là thành ngữ để truyền một vectơ không được sửa đổi. Vấn đề bây giờ là điều đó f( vector<int>(5) )sẽ không chính xác và người dùng sẽ phải cung cấp một quá tải khác void f( vector<int> v ) { f(v); }, điều này là không đáng kể.
David Rodríguez - dribeas

1
... bây giờ vì điều đó sẽ gây khó chịu cho người dùng ngôn ngữ, các nhà thiết kế đã quyết định rằng trình biên dịch sẽ thực hiện thao tác tương đương cho bạn, trong cuộc gọi f( vector<int>(5) ), trình biên dịch tạo một tham chiếu tạm thời và sau đó liên kết tham chiếu với tạm thời đó, và tương tự nếu có một chuyển đổi ngầm từ 5trực tiếp. Điều này cho phép trình biên dịch tạo ra một chữ ký duy nhất cho hàm và cho phép một người dùng triển khai hàm duy nhất. Từ đó trở đi, hành vi tương tự được xác định cho phần còn lại của việc sử dụng các tham chiếu không đổi để có tính nhất quán.
David Rodríguez - dribeas

1
@ MichaelKrelin-hacker: tham chiếu là bí danh của đối tượng và giá trị không phải là đối tượng. Tùy thuộc vào ngữ cảnh, các tham chiếu có thể chỉ là bí danh mà trình biên dịch loại bỏ tham chiếu và chỉ sử dụng định danh để có nghĩa là bất kỳ ý nghĩa nào của đối tượng ban đầu ( T const & r = *ptr;, bất kỳ cách sử dụng nào sau này rtrong hàm đều có thể được thay thế *ptrrkhông cần tồn tại trong thời gian chạy) hoặc nó có thể phải được triển khai bằng cách giữ địa chỉ của đối tượng mà nó là bí danh (coi việc lưu trữ một tham chiếu như một thành viên của một đối tượng) - mà nó được triển khai dưới dạng một con trỏ được tham chiếu tự động.
David Rodríguez - dribeas

4

Liên kết tham chiếu không const và const tuân theo các quy tắc khác nhau

Đây là các quy tắc của ngôn ngữ C ++:

  • một biểu thức bao gồm một số chữ ( 12) là một "rvalue"
  • không được phép tạo tham chiếu không phải const với giá trị rvalue: int &ri = 12;là sai
  • được phép tạo một tham chiếu const với giá trị rvalue: trong trường hợp này, một đối tượng không tên được tạo bởi trình biên dịch; đối tượng này sẽ tồn tại miễn là bản thân tham chiếu tồn tại.

Bạn phải hiểu rằng đây là các quy tắc C ++. Họ chỉ là.

Thật dễ dàng để phát minh ra một ngôn ngữ khác, chẳng hạn như C ++ ', với các quy tắc hơi khác. Trong C ++ ', nó sẽ được phép tạo một tham chiếu không phải const với một giá trị rvalue. Không có gì mâu thuẫn hoặc không thể ở đây.

Nhưng nó sẽ cho phép một số mã rủi ro mà lập trình viên có thể không đạt được những gì anh ta dự định, và các nhà thiết kế C ++ đã quyết định đúng để tránh rủi ro đó.


0

Tham chiếu là "con trỏ ẩn" (không phải null) đến những thứ có thể thay đổi (giá trị). Bạn không thể định nghĩa chúng thành một hằng số. Nó phải là một thứ "biến".

BIÊN TẬP::

Tôi đang nghĩ về

int &x = y;

gần như tương đương với

int* __px = &y;
#define x (*__px)

đâu __pxlà tên mới và #define xchỉ hoạt động bên trong khối chứa khai báo xtham chiếu.


1
Tại sao bạn có thể, nếu tài liệu tham khảo là const:)
Michael Krelin - hacker

Nhưng ví dụ của người đăng thì khôngconst
Basile Starynkevitch

Vâng, ý tôi là từ ngữ của bạn "tài liệu tham khảo là con trỏ đến những thứ có thể thay đổi" - đó là về tài liệu tham khảo không mất phí.
Michael Krelin - hacker

" Tài liệu tham khảo là 'con trỏ ẩn' " sai " để điều đó có thể thay đổi " sai " điều đó có thể thay đổi (lvalues). " Sai
curiousguy

@Loki: bạn có thể giải thích thêm không?
Basile Starynkevitch
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.