Câu hỏi 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?


C ++ 0x sẽ làm cho mã sau và mã tương tự bị hình thành, bởi vì nó đòi hỏi một cái gọi là thu hẹp chuyển đổi của một double đến một int.

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 của n3225

Chuyển đổi thu hẹp là chuyển đổi tiềm ẩn

  • từ một loại dấu phẩy động đến một loại số nguyên, hoặc
  • từ dài gấp đôi đến float hoặc float, hoặc từ double to float, ngoại trừ nơi nguồn là biểu thức không đổi và giá trị thực sau khi chuyển đổi nằm trong phạm vi giá trị có thể được biểu diễn (ngay cả khi nó không thể được biểu diễn chính xác), hoặc
  • từ kiểu số nguyên hoặc kiểu liệt kê chưa được sắp xếp đến loại điểm, trừ khi nguồn là biểu thức không đổi và giá trị thực sau khi chuyển đổi sẽ khớp với loại mục tiêu và sẽ tạo giá trị ban đầu khi được chuyển đổi về kiểu gốc hoặc
  • từ kiểu số nguyên hoặc kiểu liệt kê chưa được sắp xếp thành kiểu số nguyên không thể đại diện cho tất cả các giá trị của loại gốc, ngoại trừ nơi nguồn là biểu thức không đổi và giá trị thực sau khi chuyển đổi sẽ khớp với loại mục tiêu và sẽ tạo giá trị ban đầu được chuyển đổi về kiểu gốc.

76
2017-12-13 22:33


gốc


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. - Johan Kotlinski
@ John Dibling: Không, việc khởi tạo không bị hỏng khi giá trị có thể được biểu diễn chính xác theo loại mục tiêu. (Và 0 đã là một int dù sao.) - aschepler
@Nim: Lưu ý rằng đây chỉ là hình thức không đúng trong vòng { -bộ khởi tạo dấu ngoặc nhọn }và việc sử dụng di sản duy nhất của chúng là dành cho mảng và cấu trúc POD. Ngoài ra, nếu mã hiện tại có các phôi rõ ràng nơi chúng thuộc về, nó sẽ không bị vỡ. - aschepler
@j_random_hacker như tờ báo đang hoạt động nói, int a = 1.0; vẫn còn hợp lệ. - Johannes Schaub - litb
@ litb: Cảm ơn. Trên thực tế tôi thấy rằng dễ hiểu nhưng đáng thất vọng - IMHO nó sẽ có được tốt hơn nhiều để yêu cầu cú pháp rõ ràng cho tất cả các chuyển đổi thu hẹp ngay từ đầu của C + +. - j_random_hacker


Các câu trả lời:


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

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

Trong chức năng void foo(const long long unsigned int&):

lỗi: thu hẹp chuyển đổi (((long long unsigned int)i) & 4294967295ull) từ long long unsigned int đến unsigned int phía trong { }

lỗi: thu hẹp chuyển đổi (((long long unsigned int)i) >> 32) từ long long unsigned int đến unsigned int phía trong { }

May mắn thay, các thông báo lỗi rất đơn giản và việc sửa lỗi 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 nằm trong thư viện bên ngoài, chỉ có 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ã. Người mới có thể được  bối rối, Tuy nhiên.


41
2018-01-14 12:25





Tôi sẽ ngạc nhiên và thất vọng trong bản thân mình để biết rằng bất kỳ mã C ++ nào tôi đã viết trong 12 năm qua đều có vấn đề như vậy. 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 thu hẹp chuyển đổi?

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

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


9
2017-12-13 22:49



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?


Tôi sẽ không ngạc nhiên nếu ai đó bị bắt bởi một cái gì đó như:

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

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

Tôi không nhớ là đã 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 là đáng tin cậy 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 hoàn toàn thuyết phục, bởi vì 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 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 cho chương trình), trong khi float asfloat[] = {val1, val2};không nên? 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 đó 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à có thể 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ẽ việc sửa lỗi luôn làm cho việc chuyển đổi trở nên 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.


7
2017-11-03 19:13



"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ó. - Georg Fritzsche


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};

Chữ số là ẩn double gây ra quảng cáo.


5
2018-03-23 10:02



do đó, làm cho nó float bằng cách viết 0.5f. ;) - underscore_d
@underscore_d Không hoạt động nếu float là một 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 các ngữ nghĩa chính xác và trở thành một lỗi với C ++ 11. Tức là, định nghĩa của một "thay đổi phá vỡ". - Jed


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

Tôi đã gặp lỗi với mã trông giống như

struct char_t {
    char a;
}

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

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

OTOH

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

là tất nhiên vẫn còn 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, thậm chí

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ủa 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à mong đợi nó hoạt động ngay cả khi (b * c)> CHAR_MAX) hoặc thu hẹp lỗi chuyển đổi (có thể là một điều tốt).


4
2018-05-20 14:56



Tôi chạy vào những điều sau đây thực sự gây phiền nhiễu vô nghĩa: unsigned char x; static 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. - Carlo Wood
"chuyển đổi số nguyên ẩn"khuyến mãi? - curiousguy


Hãy thử thêm -Không thu hẹp vào CFLAGS của bạn, ví dụ:

CFLAGS += -std=c++0x -Wno-narrowing

2
2018-05-29 08:49





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


1