2015-01-08 35 views
13

g ++ 4.9.0 -O2 -std = C++ 11cảnh báo: thu hẹp chuyển đổi C++ 11

template<class T> 
struct vec3 { 
    T x, y, z; 
    vec3() = default; 
    vec3(const vec3<T> &other) = default; 
    vec3(T xx, T yy, T zz) { x = xx; y = yy; z = zz; } 
    vec3<T> operator-(const vec3<T> &other) { 
     return vec3<T>{ x - other.x, y - other.y, z - other.z }; 
    } 
}; 

int main() { 
    vec3<char> pos{ 0, 0, 0 }; 
    vec3<char> newPos{ 0, 0, 0 }; 
    auto p = pos - newPos; 

    return 0; 
} 

tôi nhận được cảnh báo:

!!warning: narrowing conversion of ‘(((int)((vec3<char>*)this)->vec3<char>::x) - ((int)other.vec3<char>::x))’ from ‘int’ to ‘char’ inside { } [-Wnarrowing] 

Nhưng nếu tôi làm điều đó với (...) insted của {...} bên trong các operator- chức năng cảnh báo biến mất. Tại sao?

+2

Hãy nhìn vào các cuộc thảo luận trong này [báo cáo lỗi GCC] (https://gcc.gnu.org/bugzilla/show_bug. cgi? id = 44500), đặc biệt là [câu trả lời của Jonathan Wakely] (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=44500#c3) –

+1

'{}' bắt thu hẹp chuyển đổi (làm cho mã bị hỏng hình thành). '()' không. Hay bạn đang hỏi tại sao nó lại thu hẹp? –

+1

Có, tại sao nó lại thu hẹp? – hidayat

Trả lời

11

Đầu tiên, tại sao lại thu hẹp? Điều đó xuất phát từ §5/10:

Nhiều toán tử nhị phân mong đợi toán hạng hoặc số đếm gây ra chuyển đổi và loại kết quả lợi nhuận theo cách tương tự. Mục đích là để mang lại một loại phổ biến, đó cũng là loại kết quả. Mô hình này được gọi là chuyển đổi số học thông thường, được định nghĩa như sau:

- [..]

- Nếu không, các chương trình khuyến mãi không thể thiếu (4.5) phải được thực hiện trên cả hai toán hạng.

nơi khuyến mãi không thể thiếu được định nghĩa trong 4.5/1:

Một prvalue của một kiểu số nguyên khác hơn bool, char16_t, char32_t, hoặc wchar_t có số nguyên chuyển đổi cấp bậc (4.13) thấp hơn xếp hạng của int có thể được chuyển đổi thành giá trị của loại int nếu int có thể đại diện cho tất cả các giá trị của loại nguồn; nếu không, giá trị prvalue nguồn có thể được chuyển đổi thành giá trị của loại unsigned int.

Trong trường hợp của chúng tôi sau đó, chúng tôi có decltype(char + char)int vì rank chuyển đổi char 's ít hơn int vì vậy cả hai được thăng int trước khi cuộc gọi đến operator+.Bây giờ, chúng tôi có int s mà chúng tôi đang chuyển đến một nhà xây dựng mất char s. Theo định nghĩa (§8.5.4/7, cụ thể 7.4):

Một thu hẹp chuyển đổi là một chuyển đổi ngầm

(7,4) - từ một loại nguyên hoặc kiểu enumeration unscoped để một kiểu số nguyên không thể đại diện cho tất cả các giá trị của kiểu gốc, ngoại trừ nơi nguồn là một biểu thức liên tục có giá trị sau khi các quảng cáo tích phân sẽ phù hợp với loại mục tiêu.

được một cách rõ ràng bị cấm trong danh sách-khởi tạo cụ thể như mỗi §8.5.4/3 (tôi nhấn mạnh, các "xem dưới đây" thực sự đề cập đến những gì tôi chỉ cần sao chép ở trên):

List- khởi tạo của một đối tượng hoặc tài liệu tham khảo của loại T được định nghĩa như sau

- [..]

- Ngược lại, nếu T là một loại lớp, nhà thầu được xem xét. Các nhà thầu được áp dụng được liệt kê và được chọn tốt nhất thông qua độ phân giải quá tải (13.3, 13.3.1.7). Nếu một chuyển đổi thu hẹp (xem bên dưới) là cần thiết để chuyển đổi bất kỳ đối số nào, chương trình bị lỗi. [...]

Đây là lý do tại sao số vec3<T>{int, int, int} của bạn cho bạn cảnh báo: chương trình khuyến mãi số nguyên yêu cầu thu hẹp chuyển đổi trên tất cả các biểu thức. Bây giờ, tuyên bố về "hình thành không đúng" đặc biệt chỉ phát sinh trong bối cảnh khởi tạo danh sách. Đây là lý do tại sao nếu bạn khởi tạo vector của bạn mà không {}s, bạn không nhìn thấy cảnh báo rằng:

vec3<T> operator-(const vec3<T> &other) { 
    // totally OK: implicit conversion from int --> char is allowed here 
    return vec3<T>(x - other.x, y - other.y, z - other.z); 
} 

Như để giải quyết vấn đề này - chỉ cần gọi các nhà xây dựng mà không cần list-khởi tạo có lẽ là giải pháp đơn giản nhất. Ngoài ra, bạn có thể tiếp tục sử dụng danh sách-khởi và chỉ template constructor của bạn:

template <typename A, typename B, typename C> 
vec3(A xx, B yy, C zz) 
: x(xx) // note these all have to be()s and not {}s for the same reason 
, y(yy) 
, z(yy) 
{ } 
4

Một vài điều đang diễn ra tại đây. Đầu tiên, cú pháp {...} cấm chuyển đổi thu hẹp tiềm ẩn. Vì vậy, sửa chữa dễ dàng là thay đổi dấu ngoặc nhọn thành dấu ngoặc đơn:

vec3<T> operator-(const vec3<T> &other) { 
    return vec3<T>(x - other.x, y - other.y, z - other.z); 
} 

Điều thứ hai xảy ra là "eh? Char trừ char là char, vấn đề là gì?" Và câu trả lời ở đây là C/C++ muốn sử dụng kích thước tự nhiên cho phép tính số học. Đó là lý do tại sao bạn thấy thông báo lỗi của mình là (int). Here is a good explanation lý do tại sao nó làm điều đó (chỉ trong trường hợp câu trả lời StackOverflow bao giờ biến mất, ông trích dẫn 6.3.1.1 của tiêu chuẩn C11).

Vì vậy, cách khác để sửa chữa mã của bạn là:

vec3<T> operator-(const vec3<T> &other) { 
    return vec3<T>{ 
    static_cast<char>(x - other.x), 
    static_cast<char>(y - other.y), 
    static_cast<char>(z - other.z) 
    }; 
} 

Bằng cách này, mục 7 trong hiệu quả Modern C++ thuyết phục tôi rằng có những lúc () là tốt hơn để khởi tạo với, và có những lúc khi {} tốt hơn. Đôi khi bạn phải nhún vai và sử dụng cái kia.

+2

Rõ ràng và ngắn gọn, giải thích chính xác lý do tại sao tôi gặp lỗi khi cố gắng khởi tạo thành viên 'uint16_t' bằng cách thêm 2' uint16_t's. (ví dụ: 'Foo foo {x + y}'). – dwanderson

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