23

xem xét mảnh này của C++ 11 mã:Thu hẹp chuyển đổi đến bool trong danh sách-khởi tạo - hành vi kỳ lạ

#include <iostream> 

struct X 
{ 
    X(bool arg) { std::cout << arg << '\n'; } 
}; 

int main() 
{ 
    double d = 7.0; 
    X x{d}; 
} 

Có một chuyển đổi thu hẹp từ một đôi để một bool trong việc khởi tạo x. Theo sự hiểu biết của tôi về tiêu chuẩn, đây là mã hình thành không đúng và chúng ta sẽ thấy một số chẩn đoán.

Visual C++ 2013 phát hành một lỗi:

error C2398: Element '1': conversion from 'double' to 'bool' requires a narrowing conversion 

Tuy nhiên, cả Clang 3.5.0 và GCC 4.9.1, sử dụng các tùy chọn sau

-Wall -Wextra -std=c++11 -pedantic 

biên dịch mã này với không có lỗi và không có cảnh báo. Chạy chương trình kết quả đầu ra là 1 (không có bất ngờ ở đó).


Bây giờ, hãy đi sâu hơn vào lãnh thổ xa lạ.

Thay đổi X(bool arg)-X(int arg) và, đột nhiên, chúng tôi đã có một lỗi từ Clang

error: type 'double' cannot be narrowed to 'int' in initializer list [-Wc++11-narrowing] 

và một cảnh báo từ GCC

warning: narrowing conversion of 'd' from 'double' to 'int' inside { } [-Wnarrowing] 

này trông giống như những gì tôi mong đợi.


Bây giờ, giữ lập luận constructor bool (nghĩa là, trở lại X(bool arg)), và thay đổi double d = 7.0; để int d = 7;. Một lần nữa, một lỗi thu hẹp từ Clang, nhưng GCC không đưa ra bất kỳ chẩn đoán nào và biên dịch mã.

Có một vài biến thể hành vi khác mà chúng ta có thể nhận được nếu chúng ta chuyển trực tiếp liên tục đến hàm tạo, một số lạ, một số được mong đợi, nhưng tôi sẽ không liệt kê chúng ở đây - câu hỏi này sẽ mất quá nhiều thời gian.


tôi muốn nói đây là một trong những trường hợp hiếm hoi khi VC++ là đúng và Clang và GCC là sai khi nói đến tiêu chuẩn phù hợp, nhưng, do các hồ sơ theo dõi tương ứng của các trình biên dịch, tôi vẫn còn rất do dự về điều này.

Các chuyên gia nghĩ gì?


tài liệu tham khảo Tiêu chuẩn (trích dẫn từ các tài liệu chuẩn cuối cùng cho C++ 11, ISO/IEC 14.882-2.011):

Trong 8.5.4 [dcl.init.list] đoạn 3, chúng tôi có:

— Otherwise, if T is a class type, constructors are considered. The applicable constructors are enumerated and the best one is chosen through overload resolution (13.3, 13.3.1.7). If a narrowing conversion (see below) is required to convert any of the arguments, the program is ill-formed.

trong phần tương tự, tại khoản 7, ta có:

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.
[ Note: As indicated above, such conversions are not allowed at the top level in list-initializations.—end note ]

trong 3.9.1 [cơ bản.cơ bản] đoạn 7, ta có:

Types bool, char, char16_t, char32_t, wchar_t, and the signed and unsigned integer types are collectively called integral types.48 A synonym for integral type is integer type.

(tôi đã bắt đầu đặt câu hỏi về tất cả mọi thứ ở giai đoạn này ...)

+0

Hey, nơi mà tất cả những ý kiến ​​đi? Một số người trong số họ chứa thông tin hữu ích để chẩn đoán vấn đề, đặc biệt đối với Clang. – bogdan

+0

Một số nhận xét đó sẽ rất hữu ích để gửi báo cáo lỗi, tôi không hiểu tại sao tất cả chúng bị xóa, có thể yêu cầu [meta] (http://meta.stackoverflow.com/) có thể hữu ích, tôi không không có thời gian. Bạn cũng có thể thử cờ tùy chỉnh nhưng bạn không biết nó sẽ hoạt động bao lâu trước khi nó được thực hiện. –

+0

@dyp không chắc chắn bạn sẽ thấy điều này vì nhận xét của bạn đã bị xóa nhưng các liên kết hữu ích của bạn với mã nguồn của tiếng kêu vang đã bị xóa và sẽ hữu ích khi đưa chúng trở lại. –

Trả lời

14

này chỉ đơn giản là trông giống như một lỗi, nếu chúng ta hãy thử như sau:

bool b {3} ; 

cả gccclang vấn đề một chẩn đoán, ví dụ gcc nói:

warning: narrowing conversion of '3' from 'int' to 'bool' inside { } [-Wnarrowing]

này được bao phủ trong draft C++11 standard bởi phần 8.5.4List-khởi đoạn mà nói:

A narrowing conversion is an implicit conversion

[...]

  • 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.

Đây là cùng một đoạn bao gồm ví dụ của bạn và các ví dụ đơn giản sau:

bool a {3.0} ; 

sẽ được bao phủ bởi viên đạn này từ đoạn 7 được trích dẫn ở trên:

  • from a floating-point type to an integer type, or

Từ đoạn 3, điều này là vô hình thành một đòi hỏi một chẩn đoán:

List-initialization of an object or reference of type T is defined as follows:

[...]

  • Otherwise, if the initializer list has a single element, the object or reference is initialized from that element; if a narrowing conversion (see below) is required to convert the element to T, the program is ill-formed.

gcc sản xuất không có chẩn đoán nhưng clang không cung cấp các cảnh báo sau đây, mặc dù không phải là cảnh báo chuyển đổi thu hẹp chúng ta sẽ thấy:

warning: implicit conversion from 'double' to 'bool' changes value from 3 to true [-Wliteral-conversion]

Lưu ý, phần 3.9.1[basic.fundamental] nói:

0.123.

Types bool, char, char16_t, char32_t, wchar_t, and the signed and unsigned integer types are collectively called integral types.48 A synonym for integral type is integer type.

Bạn nên gửi báo cáo lỗi với cả hai số clanggcc.

Jonathan Wakely lưu ý rằng trình biên dịch EDG đưa ra một lỗi thu hẹp cho mã OPs, đây là dấu hiệu mạnh mẽ cho thấy điều này thực sự sẽ tạo ra một chẩn đoán.

Cập nhật

Tôi đã gửi một báo cáo lỗi gccclang.

Các clang bug report has been updated as fixed:

Fixed in r229792.

+0

Vâng, cảnh báo để khởi tạo 'bool' với một * hằng số *' double' nằm dưới 'vài biến thể hành vi khác' mà tôi đã đề cập trong câu hỏi. Về cơ bản, trong trường hợp này, Clang vẫn không phát hiện ra rằng đây là một chuyển đổi thu hẹp trong danh sách khởi tạo, nhưng rơi trở lại cùng một cảnh báo mà nó phát sinh cho bất kỳ khởi tạo nào như vậy với giá trị không đổi (nó sẽ đưa ra cảnh báo tương tự nếu bạn thay thế các dấu ngoặc đơn có dấu ngoặc đơn, làm cho điều này trở thành một chuyển đổi ẩn được cho phép). – bogdan

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