2012-04-23 37 views
12

Tôi thiết lập một trường hợp thử nghiệm để tìm hiểu về chuyển tiếp hoàn hảo.C++ 11: sự tinh tế của std :: forward: Danh tính có thực sự cần thiết không?

std::string inner(const std::string& str) { 
return "const std::string&"; 
} 
std::string inner(std::string& str) { 
    return "std::string&"; 
} 
std::string inner(const std::string&& str) { 
    return "const std::string&&"; 
} 
std::string inner(std::string&& str) { 
    return "std::string&&"; 
} 

template <typename T> void outer(T&& t) { 
    std::cout << "t: " << t << std::endl; 
    std::cout << "perfect forward: " << inner(std::forward<T>(t)) << std::endl; 
    std::cout << std::endl; 
} 

void PerfectForwarding() 
{ 
    outer("literal"); 
    outer(lvalue); 
    outer(constlvalue); 
    outer(rvalue()); 
    outer(constrvalue()); 
} 

std::forward hoạt động như mong đợi. Các hành vi thú vị xuất hiện khi tôi thực hiện chức năng chuyển tiếp của riêng tôi mà không sắc:

template <typename T> T&& MyForward(T& t) 
{ 
    return ((T&&)t); 
} 

Thay std::forward với MyForward ở bên ngoài cung cấp cho kết quả chính xác cùng! Hành vi này đặt ra câu hỏi tại sao danh tính được sử dụng?

Compiler VS2010

Cập nhật 1: Trong tài liệu tham khảo để ngăn chặn loại trừ

AFAIK, các quy tắc loại trừ đặc biệt chỉ được kích hoạt trên T & &. Lưu ý định nghĩa về phía trước, forward(typename identity<T>::type& t). Loại đối số chỉ có một &. Trong thực tế, sau khi tôi thay đổi MyForward để sử dụng danh tính, và bỏ ra (T & &) truyền, ví dụ không biên dịch. Trên bề mặt, việc đúc từ lvalue đến rvalue dường như làm cho công việc tiến lên.

Cập nhật 2: được thử nghiệm trên ideone.com với GCC 4.5, cùng một hành vi.

+1

Bạn có ý nghĩa gì với "danh tính"? Hai quá tải của 'std :: forward' hoặc' remove_reference :: type'? – kennytm

+1

danh tính đề cập đến cấu trúc nhận dạng, loại đối số chuyển tiếp: _Ty && forward (typename identity <_Ty> :: type & _Arg) –

+2

Đó phải là một số chữ ký chuẩn. Một tiêu chuẩn là 'T && forward (typename remove_reference :: type & t)' (và quá tải lấy '&&'). – kennytm

Trả lời

13

remove_reference<T> (identity là trong một phiên bản cũ của dự thảo, nhưng đã được đổi thành remove_reference) được sử dụng để ngăn chặn loại trừ: std::forwardchỉ làm việc với một tham số kiểu tường minh. Nếu không, phần sau sẽ biên dịch:

std::forward(t) 

... nhưng nó sẽ không làm điều đúng.

Về vấn đề với giá trị/giá trị, vui lòng lưu ý rằng có two overloads of std::forward: một cho giá trị, một cho giá trị.

Thực tế, việc triển khai MyForward được cung cấp giống như std::move: nó biến giá trị thành giá trị (sự khác biệt là di chuyển cũng chấp nhận giá trị).

+0

vui lòng xem cập nhật. –

+0

chỉ có một VS2010 này. Và nó hoạt động với một. thì tại sao chúng ta cần hai? –

+0

@CandyChiu nó hoạt động trên VS2010 vì nó đã được vận chuyển trước khi tiêu chuẩn được hoàn thành. Các quy tắc đã thay đổi giữa thời gian vận chuyển VS2010 và tiêu chuẩn đã được xuất bản. Chúng ta cần hai vì các giá trị không liên kết với '&&', và các giá trị sẽ không ràng buộc với '&'. –

0

Tôi đã kiểm tra định nghĩa của forwardidentity trong VS 2010. Sự khác biệt duy nhất giữa MyForward của bạn và forward họ là bạn sử dụng một tham số T& và họ sử dụng một tham số typename identity<T>::type&. Và số identity<T>::type chỉ là T.

Hiệu ứng quan trọng nhất (cũng có thể chỉ) của sự khác biệt này là sử dụng đối số mẫu forward của bạn phải được chỉ định rõ ràng trong khi đối số mẫu của MyForward có thể được suy ra từ cuộc gọi.

+0

"có thể được suy ra" ... tốt, nó sẽ được suy ra không chính xác –

+0

Có, nếu vượt qua một lvalue để bên ngoài (T &&), t sẽ được đúc thành một loại tham chiếu rvalue. – Cosyn

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