2013-06-04 36 views
16

Xét đoạn mã sau:giải thích tham khảo trong C++

#include <iostream> 

template<typename T> 
void inc1(T&& x) 
{ 
    T y = x; 
    ++y; 
} 

template<typename T> 
void inc2(T& x) 
{ 
    T y = x; 
    ++y; 
} 

int main() 
{ 
    int a = 10; 
    inc1(a); // a is now 11 

    int b = 10; 
    inc2(b); // b remains 10 
} 

Sau khi thay thế chúng tôi có

void inc1(int& x) 
{ 
    int& y = x; // reference to x 
    ++y; // increments x 
} 

void inc2(int& x) 
{ 
    int y = x; // copy of x 
    ++y; // increments the copie 
} 

Trong inc1, x là loại int& vì cả hai int&T&& là tài liệu tham khảo nhưng không phải cả hai đều là r -giá trị.

Tương tự, trong inc2, x là loại int&, bởi vì một lần nữa cả hai int&T& là tài liệu tham khảo, nhưng không phải cả hai đều là r-giá trị.

Câu hỏi của tôi là về y: tại sao trong inc1, y là loại int&, trong khi ở inc2, y là loại int?

Tôi đã quan sát điều này trên cả gcc 4.8.1 và microsoft v110 và v120_ctp.

Trả lời

14

Trong cả hai cuộc gọi chức năng, những gì bạn chuyển đến hàm là int & (theo nghĩa: "giá trị của loại int"). Vì vậy, được trình bày với tuyên bố của inc1, trình biên dịch phải khấu trừ T sao cho T && khớp với đối số bạn đã cung cấp, tức là int &. Cách duy nhất để làm điều này là giả định rằng Tint &, bởi vì sau đó T &&int & &&, tương đương với int &. Vì vậy, T trở thành int & và địa phương y được khai báo là như vậy.

Mặt khác, trong inc2, trình biên dịch phải suy TT & trận đấu kiểu lập luận mà bạn cung cấp, mà vẫn còn int &. Điều đó được thực hiện dễ dàng nhất bằng cách giả sử T chỉ đơn giản là int, do đó, đó là những gì bạn nhận được cho loại địa phương y sau đó.


Trả lời một vài ý kiến ​​(đã đồng thời bị xóa): Nếu bạn có một chức năng với một kiểu lập luận xác định trước, chẳng hạn như

sau đó, khi bạn gọi đây là ví dụ như inc3(a), trình biên dịch sẽ áp dụng bất kỳ chuyển đổi ngầm nào cần thiết thành đối số để làm cho nó phù hợp. Trong trường hợp của inc3(a), điều này có nghĩa là chuyển đổi a từ int & (theo nghĩa của lvalue) sang int (theo nghĩa rvalue) – được gọi là chuyển đổi lvalue thành rvalue và chuyển đổi tiềm ẩn hợp lệ. Về cơ bản nó là số tiền để biến biến a thành giá trị mà nó đại diện tại thời điểm đó.

Nhưng khi bạn khai báo một mẫu, chẳng hạn như inc1inc2 từ câu hỏi, và đối số chức năng được xác định trong điều khoản của một số mẫu, sau đó trình biên dịch sẽ không, hay không mà thôi, cố gắng áp dụng chuyển đổi tiềm ẩn đối số để làm cho nó phù hợp.Thay vào đó, nó sẽ chọn thông số loại đối số T để nó khớp với loại đối số bạn đã cung cấp. Các quy tắc cho điều này phức tạp, nhưng trong trường hợp khai báo đối số loại T &&, chúng hoạt động như mô tả ở trên. (Trong trường hợp của một tuyên bố T tranh luận thuần túy, một cuộc tranh cãi giá trị trái vẫn sẽ trải qua vế trái-to-rvalue chuyển đổi, và T sẽ được suy luận như int, không int &.)

Đây là lý do tại sao, trong khi int && là một tài liệu tham khảo rvalue , T && (trong đó T là thông số mẫu) không nhất thiết phải là tham chiếu giá trị. Thay vào đó, đó là bất kỳ kết quả nào từ việc lắp T vào đối số được cung cấp. Do đó, biểu thức T && trong trường hợp này được gọi là tham chiếu chung (như được đối chiếu với tham chiếu giá trị hoặc giá trị) – nó là tham chiếu trở thành giá trị hoặc giá trị, nếu cần.

+0

Tại sao bạn nói chúng tôi chuyển 'int &'? Điều này có hiển nhiên không? – perreal

+0

@perreal Có. Các đối số được truyền là các biến được khai báo là 'int':' int a = 10; 'và' int b = 10; ', tương ứng. Khi các biến như vậy được sử dụng làm đối số cho các cuộc gọi hàm, kiểu của chúng là 'int &' (tức là chúng là tham chiếu lvalue). – jogojapan

+1

Để sửa chú thích trước của tôi: Kiểu của các biến này thực sự là 'int &', vì vậy chúng là _lvalues_ (không phải tham chiếu lvalue. Thuật ngữ này chỉ áp dụng cho các biến mới được khai báo là tham chiếu đến 'a' hoặc' b'). – jogojapan

7

S14.8.2.1 [temp.deduct.call] nói:

Template trích lập luận được thực hiện bằng cách so sánh từng chức năng kiểu mẫu tham số (gọi nó là P) với kiểu của đối số tương ứng của cuộc gọi (gọi là A) như được mô tả bên dưới.

Vì vậy, chúng tôi đang cố gắng tìm ra P cho loại int.

S14.8.2.3 tiếp tục:

Nếu P là một loại cv-đủ điều kiện, cấp cao nhất cv-vòng loại P được bỏ qua cho loại trừ. Nếu P là loại tham chiếu , loại được tham chiếu bằng P được sử dụng để khấu trừ loại. Nếu P là tham chiếu rvalue đối với tham số mẫu cv-unqualified và đối số là một lvalue, loại "tham chiếu lvalue đến A" được sử dụng thay cho A để khấu trừ loại. [Ví dụ:

template <class T> int f(T&&);   // <--- YOUR TEMPLATE IS LIKE THIS 
template <class T> int g(const T&&); 
int i; 
int n1 = f(i); // calls f<int&>(int&) // <--- YOUR CALL IS LIKE THIS 
int n2 = f(0); // calls f<int>(int&&) 
int n3 = g(i); // error: would call g<int>(const int&&), which 
       // would bind an rvalue reference to an lvalue 

-end dụ]

Cuộc gọi của bạn cũng giống như f(i) trong ví dụ - mà instantiates một hàm có dạng f<int&>(int&) ... tức là Tint& , đó là lý do tại sao T y = x tạo tham chiếu đến x.

Xem thêm trang của Scott Meyers tại http://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers