[英]warning: narrowing conversion C++11
g ++ 4.9.0 -O2 -std = 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;
}
我收到警告:
!!warning: narrowing conversion of ‘(((int)((vec3<char>*)this)->vec3<char>::x) - ((int)other.vec3<char>::x))’ from ‘int’ to ‘char’ inside { } [-Wnarrowing]
但如果我在operator-
功能內部(...)
插入{...}
,警告就會消失。 為什么?
首先,為何縮小? 這來自§5/ 10:
許多期望算術或枚舉類型的操作數的二元運算符會以類似的方式引起轉換並產生結果類型。 目的是產生一個通用類型,它也是結果的類型。 這種模式稱為通常的算術轉換 ,定義如下:
- [..]
- 否則,應對兩個操作數執行整數促銷(4.5)。
整體促銷在4.5 / 1中定義:
如果
int
可以表示源類型的所有值,則除了bool
,char16_t
,char32_t
或wchar_t
之外的整數類型的prvalue(整數轉換等級(4.13)小於int
的等級)可以轉換為int
類型的prvalue ; 否則,源prvalue可以轉換為unsigned int
類型的prvalue。
在我們的例子中,我們有decltype(char + char)
是int
因為char
的轉換等級小於int
所以在調用operator+
之前都將它們提升為int
。 現在,我們有int
,我們傳遞給一個帶char
的構造函數。 根據定義(§8.5.4/ 7,特別是7.4):
縮小轉換是隱式轉換
(7.4) - 從整數類型或未范圍的枚舉類型到不能表示原始類型的所有值的整數類型,除非源是一個常量表達式,其整數提升后的值將適合目標類型。
根據§8.5.4/ 3特別明確禁止列表初始化(強調我的,“見下文”實際上是指我剛剛復制的內容):
列表初始化對象或類型
T
引用定義如下- [..]
- 否則,如果
T
是類類型,則考慮構造函數。 枚舉適用的構造函數,並通過重載決策(13.3,13.3.1.7)選擇最佳構造函數。 如果轉換任何參數需要縮小轉換(見下文),則程序格式錯誤 。 [...]
這就是你的vec3<T>{int, int, int}
給你一個警告的原因:由於整數提升需要對所有表達式進行縮小轉換,程序vec3<T>{int, int, int}
不正確。 現在,關於“格式錯誤”的陳述僅在列表初始化的上下文中出現。 這就是為什么如果你在沒有{}s
情況下初始化你的矢量,你就不會看到這個警告:
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 );
}
至於解決這個問題 - 只是在沒有列表初始化的情況下調用構造函數可能是最簡單的解決方案。 或者,您可以繼續使用列表初始化,只需模板化構造函數:
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)
{ }
這里有幾件事情正在發生。 首先, {...}
語法禁止隱式縮小轉換。 因此,簡單的解決方法是將大括號更改為括號:
vec3<T> operator-(const vec3<T> &other) {
return vec3<T>( x - other.x, y - other.y, z - other.z );
}
第二件事是,“呃?char減去一個char就是char,問題是什么?!” 這里的答案是C / C ++想要使用自然大小進行算術運算。 這就是您在錯誤消息中看到(int)
強制轉換的原因。 這里有一個很好的解釋為什么它這樣做(只是在StackOverflow答案消失的情況下,他引用了C11標准的6.3.1.1)。
因此,修復代碼的另一種方法是:
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)
};
}
順便說一下,Effective Modern C ++中的第7項讓我確信有時候()
更好地初始化,有時{}
更好。 有時你只需聳肩並使用另一個。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.