2010-12-13 24 views
75

C++ 0x sẽ làm cho mã sau và mã tương tự không đúng định dạng, vì nó yêu cầu một chuyển đổi thu hẹp thu hẹp của một double thành một số int.Thu hẹp chuyển đổi trong C++ 0x. Chỉ là tôi, hay âm thanh này như một sự thay đổi đột ngột?

int a[] = { 1.0 }; 

Tôi tự hỏi liệu kiểu khởi tạo này có được sử dụng nhiều trong mã thế giới thực hay không. Có bao nhiêu mã sẽ bị phá vỡ bởi thay đổi này? Có rất nhiều nỗ lực để sửa lỗi này trong mã của bạn, nếu mã của bạn bị ảnh hưởng chút nào?


Để tham khảo, xem 8.5.4/6 n3225

A narrowing conversion is an implicit conversion

  • from a floating-point type to an integer type, or
  • from long double to double or float, or from double to float, except where the source is a constant expression and the actual value after conversion is within the range of values that can be represented (even if it cannot be represented exactly), or
  • from an integer type or unscoped enumeration type to a floating-point type, except where the source is a constant expression and the actual value after conversion will fit into the target type and will produce the original value when converted back to the original type, or
  • from an integer type or unscoped enumeration type to an integer type that cannot represent all the values of the original type, except where the source is a constant expression and the actual value after conversion will fit into the target type and will produce the original value when converted back to the original type.
+0

cho phép hy vọng không, tôi không thấy loại này khởi tạo nhưng họ yên tâm rằng họ đang cố gắng tốt nhất của họ để không phá vỡ bất kỳ mã-base , C++ 0x có hiệu ứng tốt, không có ít hơn –

+0

@litb: Điều này cũng sẽ bị hỏng, hay chỉ là khi có chuyển đổi xảy ra? 'int a [10] = {0};' –

+1

Giả sử điều này là hợp lệ chỉ để khởi tạo các loại sẵn có, tôi không thể thấy điều này sẽ gây hại như thế nào. Chắc chắn, điều này có thể phá vỡ một số mã. Nhưng nên dễ sửa. –

Trả lời

40

Tôi đã gặp phải sự thay đổi đột phá này khi tôi sử dụng GCC.Trình biên dịch in lỗi cho mã như thế này:

void foo(const unsigned long long &i) 
{ 
    unsigned int a[2] = {i & 0xFFFFFFFF, i >> 32}; 
} 

In function void foo(const long long unsigned int&) :

error: narrowing conversion of (((long long unsigned int)i) & 4294967295ull) from long long unsigned int to unsigned int inside { }

error: narrowing conversion of (((long long unsigned int)i) >> 32) from long long unsigned int to unsigned int inside { }

May mắn thay, các thông báo lỗi là đơn giản và sửa chữa rất đơn giản:

void foo(const unsigned long long &i) 
{ 
    unsigned int a[2] = {static_cast<unsigned int>(i & 0xFFFFFFFF), 
      static_cast<unsigned int>(i >> 32)}; 
} 

Mã này là trong một thư viện bên ngoài, chỉ với hai lần xuất hiện trong một tệp. Tôi không nghĩ rằng sự thay đổi phá vỡ sẽ ảnh hưởng đến nhiều mã. Tuy nhiên, người mới có thể getconfused,.

9

Tôi sẽ ngạc nhiên và thất vọng về bản thân mình khi biết rằng bất kỳ mã C++ tôi đã viết trong vòng 12 năm trở lại đây đã có điều này loại vấn đề. Nhưng hầu hết các trình biên dịch sẽ có những lời cảnh báo về bất kỳ thời gian biên dịch "thu hẹp" tất cả cùng, trừ khi tôi đang thiếu một cái gì đó.

Chúng cũng có thu hẹp chuyển đổi không?

unsigned short b[] = { -1, INT_MAX }; 

Nếu vậy, tôi nghĩ chúng có thể xuất hiện thường xuyên hơn một chút so với loại nổi của bạn thành ví dụ không thể tách rời.

+1

Tôi không hiểu tại sao bạn nói điều này sẽ là một điều không phổ biến để tìm trong mã. Logic giữa sử dụng -1 hoặc INT_MAX thay vì USHRT_MAX là gì? Có phải USHRT_MAX không nằm trong vùng khí hậu vào cuối năm 2010 không? –

7

tôi sẽ không tất cả những gì ngạc nhiên nếu ai đó bị kẹt ra bởi một cái gì đó như:

float ra[] = {0, CHAR_MAX, SHORT_MAX, INT_MAX, LONG_MAX}; 

(tình hình thực hiện của tôi, cuối cùng hai không tạo ra kết quả tương tự khi chuyển đổi trở lại int/dài , do đó đang thu hẹp)

Tôi không nhớ đã từng viết bài này. Nó chỉ hữu ích nếu một xấp xỉ với các giới hạn là hữu ích cho một cái gì đó.

Điều này có vẻ ít nhất mơ hồ chính đáng quá:

void some_function(int val1, int val2) { 
    float asfloat[] = {val1, val2}; // not in C++0x 
    double asdouble[] = {val1, val2}; // not in C++0x 
    int asint[] = {val1, val2};  // OK 
    // now do something with the arrays 
} 

nhưng nó không phải là hoàn toàn thuyết phục, vì nếu tôi biết tôi có chính xác hai giá trị, tại sao đặt chúng trong mảng thay vì chỉ float floatval1 = val1, floatval1 = val2;? Động lực là gì, tuy nhiên, tại sao phải biên dịch (và làm việc, với điều kiện mất độ chính xác nằm trong độ chính xác chấp nhận được đối với chương trình), trong khi float asfloat[] = {val1, val2}; thì không? Dù bằng cách nào tôi đang khởi tạo hai phao từ hai ints, nó chỉ là trong một trường hợp hai nổi xảy ra là thành viên của một tổng hợp. Điều này có vẻ đặc biệt khắc nghiệt trong trường hợp biểu thức không liên tục dẫn đến chuyển đổi thu hẹp mặc dù (trên một triển khai cụ thể), tất cả các giá trị của loại nguồn đều thể hiện trong loại đích và chuyển đổi về giá trị ban đầu của chúng:

char i = something(); 
static_assert(CHAR_BIT == 8); 
double ra[] = {i}; // how is this worse than using a constant value? 

Giả sử không có lỗi, có lẽ sửa chữa luôn làm cho chuyển đổi rõ ràng. Trừ khi bạn đang làm một cái gì đó kỳ lạ với macro, tôi nghĩ rằng một bộ khởi tạo mảng chỉ xuất hiện gần loại mảng, hoặc ít nhất là một cái gì đó đại diện cho loại, mà có thể phụ thuộc vào một tham số mẫu. Vì vậy, một diễn viên nên được dễ dàng, nếu tiết.

+8

* "nếu tôi biết tôi có chính xác hai giá trị, tại sao đặt chúng vào mảng" * - ví dụ: vì một API như OpenGL yêu cầu nó. –

4

Lỗi chuyển đổi thu hẹp tương tác tồi tệ với quy tắc quảng cáo số nguyên tiềm ẩn.

Tôi đã có một lỗi với mã mà trông giống như

struct char_t { 
    char a; 
} 

void function(char c, char d) { 
    char_t a = { c+d }; 
} 

nào tạo ra một lỗi chuyển đổi hẹp (đó là chính xác theo tiêu chuẩn). Lý do là cd ngầm được thăng cấp lên int và kết quả là int không được phép thu hẹp lại thành char trong danh sách bộ khởi tạo.

OTOH

void function(char c, char d) { 
    char a = c+d; 
} 

là tất nhiên vẫn sử dụng tốt (nếu không tất cả địa ngục sẽ phá vỡ lỏng lẻo). Nhưng đáng ngạc nhiên, ngay cả

template<char c, char d> 
void function() { 
    char_t a = { c+d }; 
} 

là ok và biên dịch mà không có cảnh báo nếu tổng c và d nhỏ hơn CHAR_MAX. Tôi vẫn nghĩ đây là một khiếm khuyết trong C++ 11, nhưng những người ở đó nghĩ khác - có thể bởi vì nó không dễ sửa mà không loại bỏ hoặc chuyển đổi số nguyên ẩn (đó là một sự phản đối từ quá khứ, khi người ta viết mã như char a=b*c/d và dự kiến ​​nó hoạt động ngay cả khi (b * c)> CHAR_MAX) hoặc thu hẹp lỗi chuyển đổi (có thể là điều tốt).

+0

Tôi chạy vào sau đây mà thực sự gây phiền nhiễu vô nghĩa: 'unsigned char x; tĩnh unsigned char const m = 0x7f; ... unsigned char r = {x & m }; '<- thu hẹp chuyển đổi bên trong {}. Có thật không? Vì vậy, các nhà điều hành & cũng ngầm chuyển đổi ký tự unsigned để int? Vâng tôi không quan tâm, kết quả vẫn được đảm bảo là một unsigned char, argh. –

1

Dường như GCC-4.7 không còn cung cấp lỗi để thu hẹp chuyển đổi mà thay vào đó là cảnh báo.

4

Một ví dụ thực tế mà tôi đã gặp phải:

float x = 4.2; // an input argument 
float a[2] = {x-0.5, x+0.5}; 

hiểu theo nghĩa đen là số ngầm double gây xúc tiến.

+1

để làm cho nó 'nổi' bằng cách viết '0,5f'. ;) –

+1

@underscore_d Không hoạt động nếu 'float' là tham số typedef hoặc template (ít nhất là không mất độ chính xác), nhưng vấn đề là mã được viết với ngữ nghĩa chính xác và trở thành lỗi với C++ 11. Tức là, định nghĩa của một "thay đổi phá vỡ". – Jed

1

Hãy thử thêm -Wno-thu hẹp để CFLAGS của bạn, ví dụ:

CFLAGS += -std=c++0x -Wno-narrowing 
Các vấn đề liên quan