2012-04-14 63 views
31

Dường như trình biên dịch coi chúng là các loại không liên quan và do đó yêu cầu reinterpret_cast. Tại sao đây là quy tắc?Tại sao tôi không thể static_cast giữa char * và unsigned char *?

+0

Tôi đang lấy hàm băm SHA-1 của một chuỗi. 'c_str()' trả về 'const char *' và hàm SHA-1 lấy một 'const unsigned char *' làm đối số. – Nick

+0

Và bạn mong đợi điều gì sẽ xảy ra nếu chuỗi đó chứa các giá trị ký tự âm? – Pubby

+0

Tôi mong đợi bất kỳ giá trị âm 'c' trở thành' c + 256', như là tiêu chuẩn trong việc chuyển đổi một byte đã ký thành một dấu chưa ký. Thành thật mà nói, tôi chỉ thực hiện chuyển đổi để tính toán giá trị băm. Tôi không quan tâm họ chuyển đổi như thế nào, miễn là chúng được chuyển đổi theo cùng một cách. – Nick

Trả lời

26

Họ là loại hoàn toàn khác nhau thấy tiêu chuẩn:

3.9.1 loại cơ bản [basic.fundamental]

1 đối tượng khai báo là ký tự char) phải đủ lớn để cửa hàng bất kỳ thành viên của bộ ký tự cơ bản của thực hiện. Nếu một ký tự từ bộ này được lưu trữ trong một đối tượng ký tự, giá trị không thể tách rời của đối tượng ký tự đó bằng với giá trị của một ký tự chữ cái duy nhất của ký tự đó là . Nó là được xác định thực hiện cho dù đối tượng char có thể giữ giá trị âmkhông. Các ký tự có thể được khai báo rõ ràng hoặc
đã ký.Đồng bằng char, ký hiệu char và char chưa ký là ba loại riêng biệt. Một char, một char đã ký, và một ký tự chưa ký chiếm cùng một lượng bộ nhớ và có cùng một liên kết yêu cầu (basic.types); có nghĩa là, họ có cùng một đối tượng đại diện . Đối với các loại ký tự, tất cả các bit của đối tượng
đại diện tham gia vào biểu diễn giá trị. Đối với các loại ký tự không được ký , tất cả các mẫu bit có thể có của đại diện giá trị đại diện cho các số. Những yêu cầu này không giữ cho các loại khác. Trong bất kỳ triển khai cụ thể nào, đối tượng char đơn giản có thể thực hiện trên các giá trị giống như char đã ký hoặc thẻ chưa ký; cái nào là được xác định thực hiện.

Vì vậy, tương tự như đây cũng là lý do tại sao thất bại sau:

unsigned int* a = new unsigned int(10); 
int* b = static_cast<int*>(a); // error different types 

ab là loại hoàn toàn khác nhau, thực sự những gì bạn đang đặt câu hỏi là tại sao được static_cast nên hạn chế khi nó có thể thực hiện sau đây mà không vấn đề

unsigned int a = new unsigned int(10); 
int b = static_cast<int>(a); // OK but may result in loss of precision 

và tại sao không thể suy ra rằng loại mục tiêu có cùng chiều rộng trường bit và có thể được biểu diễn? Nó có thể làm điều này cho các loại vô hướng nhưng đối với con trỏ, trừ khi đích được bắt nguồn từ nguồn và bạn muốn thực hiện một downcast thì việc đúc giữa các con trỏ sẽ không hoạt động.

Bjarne Stroustrop nêu rõ lý do tại sao static_cast hữu ích trong liên kết này: http://www.stroustrup.com/bs_faq2.html#static-cast nhưng dưới dạng viết tắt nó là để người dùng nêu rõ ý định của họ là gì và để cho trình biên dịch có cơ hội kiểm tra xem bạn đang có ý định gì đạt được, vì static_cast không hỗ trợ truyền giữa các loại con trỏ khác nhau, trình biên dịch có thể bắt lỗi này để cảnh báo người dùng và nếu họ thực sự muốn thực hiện chuyển đổi này thì họ nên sử dụng reinterpret_cast.

+0

thx để nêu tiêu chuẩn ở đây. Tôi không có nó. –

+0

Vì vậy, nếu chúng là các kiểu riêng biệt, tại sao trình biên dịch cho phép phép đúc 'unsigned char a = 255; char b = static_cast (a); '? – Nick

+2

cùng lý do bạn có thể static_cast từ tăng gấp đôi thành int và cách khác, những gì bạn không thể làm là static_cast double * thành int *, các loại con trỏ khác nhau nhưng bạn có thể chuyển đổi từ giá trị này sang giá trị khác. bị mất chính xác – EdChum

7

bạn đang cố gắng chuyển đổi các con trỏ không liên quan bằng static_cast. Đó không phải là những gì static_cast là cho. Ở đây bạn có thể thấy: Type Casting.

Với static_cast bạn có thể chuyển đổi dữ liệu số (ví dụ: char thành unsigned char sẽ hoạt động) hoặc trỏ đến các lớp liên quan (liên quan bởi một số thừa kế). Đây là cả hai không phải là trường hợp. Bạn muốn chuyển đổi một con trỏ không liên quan đến con trỏ khác để bạn phải sử dụng reinterpret_cast.

Về cơ bản những gì bạn đang cố gắng làm là dành cho trình biên dịch giống như cố gắng chuyển đổi char * thành void *.


Ok, đây là một số suy nghĩ bổ sung tại sao cho phép điều này về cơ bản là sai. static_cast có thể được sử dụng để chuyển đổi các loại số thành nhau. Vì vậy, nó là hoàn toàn hợp pháp để viết như sau:

char x = 5; 
unsigned char y = static_cast<unsigned char>(x); 

gì cũng có thể:

double d = 1.2; 
int i = static_cast<int>(d); 

Nếu bạn nhìn vào mã này trong assembler bạn sẽ thấy rằng các diễn viên thứ hai không phải là chỉ một tái -Giải thích mô hình bit của d nhưng thay vào đó một số hướng dẫn lắp ráp cho các chuyển đổi được chèn vào đây.

Bây giờ nếu chúng ta mở rộng hành vi này thành mảng, trường hợp đơn giản là cách giải thích mẫu bit khác nhau là đủ, nó có thể hoạt động. Nhưng những gì về mảng mảng tăng gấp đôi để mảng ints? Đó là nơi bạn hoặc phải tuyên bố rằng bạn đơn giản muốn có một sự giải thích lại các mẫu bit - có một cơ chế được gọi là reinterpret_cast, hoặc bạn phải thực hiện thêm một số công việc. Như bạn có thể thấy đơn giản mở rộng static_cast cho con trỏ/mảng là không đủ vì nó cần phải hành xử tương tự như static_casting giá trị duy nhất của các loại. Điều này đôi khi cần thêm mã và nó không rõ ràng làm thế nào điều này nên được thực hiện cho mảng. Trong trường hợp của bạn - dừng lại ở \ 0 - bởi vì đó là quy ước? Điều này là không đủ cho các trường hợp không phải chuỗi (số). Điều gì sẽ xảy ra nếu kích thước của kiểu dữ liệu thay đổi (ví dụ: int so với double trên x86-32bit)?

Hành vi bạn muốn không thể được xác định đúng cho tất cả các trường hợp sử dụng, đó là lý do tại sao nó không nằm trong tiêu chuẩn C++. Nếu không, bạn sẽ phải nhớ những thứ như: "tôi có thể đúc loại này đến khác miễn là chúng có kiểu số nguyên, có cùng chiều rộng và ...". Bằng cách này, nó hoàn toàn rõ ràng - hoặc là các lớp có liên quan - sau đó bạn có thể truyền các con trỏ, hoặc chúng là các kiểu số - sau đó bạn có thể truyền các giá trị.

+0

Tôi thừa nhận trong bài viết mở đầu của tôi rằng trình biên dịch nói rằng chúng là các con trỏ không liên quan. Những gì tôi muốn biết là _why_. Dường như với tôi rằng nếu 'T1' là" liên quan "với' T2', thì 'T1 *' phải là "liên quan" thành 'T2 *'. Tại sao không phải là âm thanh quy tắc đánh máy (đối với các kiểu nguyên thủy)? – Nick

+3

@Nick nó không phải là "bằng cách nào đó liên quan" nhưng "lớp học liên quan". Như bạn đã nói, char và unsigned char là nguyên thủy - không phải lớp. Đó là lý do - và đó là những gì tôi nói nếu bạn đọc kỹ. Bạn nói đúng - nếu lớp T1 có liên quan đến lớp T2, thì bạn có thể sử dụng static_cast để chuyển đổi T1 * sang T2 *. Đây không phải là những gì bạn đang làm. char không liên quan đến unsigned char theo nghĩa của mối quan hệ theo yêu cầu của tiêu chuẩn C++. –

+0

Tuy nhiên, nếu bạn thả con trỏ, trình biên dịch không có vấn đề gì giữa các kiểu nguyên thủy, ví dụ: 'unsigned char a = 255; char b = static_cast (a); 'Có vẻ hơi kỳ lạ, vì nếu' T1' và 'T2' là các lớp, diễn viên giữa các con trỏ không phải là âm thanh, vì bạn có thể làm như sau: ' class A; ' 'lớp B: công khai A;' 'B * b = new B [4]; ' ' b [0] = B();' 'A * a = static_cast (b); ' ' a [1] = A(); ' ' B b1 = b [1]; // oops' Có vẻ như thời gian _only_ dàn diễn viên phải an toàn giữa các loại nguyên thủy. – Nick

4

Bên cạnh là con trỏ, unsigned char *char * có gì chung (EdChum đã đề cập đến thực tế là char, signed charunsigned char ba loại khác nhau). Bạn có thể nói điều tương tự cho các loại con trỏ Foo *Bar * cho bất kỳ cấu trúc không giống nhau nào.

static_cast có nghĩa là con trỏ của loại nguồn có thể được sử dụng làm con trỏ của loại đích, yêu cầu mối quan hệ loại con. Do đó nó không thể được sử dụng trong bối cảnh của câu hỏi của bạn; những gì bạn cần là reinterpret_cast thực hiện chính xác những gì bạn muốn hoặc dàn diễn viên kiểu C.

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