警告:缩小转换 C++11
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-
函数中使用 (...)
insted of {...}
来做到这一点,警告就会消失。为什么?
这里发生了几件事。首先,{...}
语法禁止隐式收缩转换。所以简单的解决方法是将大括号更改为圆括号:
vec3<T> operator-(const vec3<T> &other) {
return vec3<T>( x - other.x, y - other.y, z - other.z );
}
第二件事是,"eh? char minus a char is a char, what is the problem?!"这里的答案是C/C++想要使用自然大小进行算术运算。这就是您在错误消息中看到 (int)
强制转换的原因。 Here is a good explanation 为什么这样做(以防 Whosebug 的答案消失,他引用了 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 项让我相信,有时 ()
更适合初始化,有时 {}
更好。有时你不得不耸耸肩,然后使用另一个。
首先,为什么缩小?来自§5/10:
Many binary operators that expect operands of arithmetic or enumeration type cause conversions and yield result types in a similar way. The purpose is to yield a common type, which is also the type of the result. This pattern is called the usual arithmetic conversions, which are defined as follows:
— [..]
— Otherwise, the integral promotions (4.5) shall be performed on both operands.
其中积分提升在 4.5/1 中定义:
A prvalue of an integer type other than bool
, char16_t
, char32_t
, or wchar_t
whose integer conversion rank (4.13) is less than the rank of int
can be converted to a prvalue of type int
if int
can represent all the values of the source type; otherwise, the source prvalue can be converted to a prvalue of type unsigned int
.
在我们的例子中,decltype(char + char)
是 int
,因为 char
的转化排名低于 int
,因此两者都被提升为 int
在调用 operator+
之前。现在,我们有 int
s,我们正在将其传递给采用 char
s 的构造函数。根据定义(§8.5.4/7,特别是 7.4):
A narrowing conversion is an implicit conversion
(7.4) — 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 whose value after integral promotions will fit into the target type.
根据 §8.5.4/3 明确禁止列表初始化(强调我的,“见下文”实际上指的是我刚刚在上面复制的内容):
List-initialization of an object or reference of type T
is defined as follows
— [..]
— 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. [...]
这就是您的 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)
{ }
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-
函数中使用 (...)
insted of {...}
来做到这一点,警告就会消失。为什么?
这里发生了几件事。首先,{...}
语法禁止隐式收缩转换。所以简单的解决方法是将大括号更改为圆括号:
vec3<T> operator-(const vec3<T> &other) {
return vec3<T>( x - other.x, y - other.y, z - other.z );
}
第二件事是,"eh? char minus a char is a char, what is the problem?!"这里的答案是C/C++想要使用自然大小进行算术运算。这就是您在错误消息中看到 (int)
强制转换的原因。 Here is a good explanation 为什么这样做(以防 Whosebug 的答案消失,他引用了 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 项让我相信,有时 ()
更适合初始化,有时 {}
更好。有时你不得不耸耸肩,然后使用另一个。
首先,为什么缩小?来自§5/10:
Many binary operators that expect operands of arithmetic or enumeration type cause conversions and yield result types in a similar way. The purpose is to yield a common type, which is also the type of the result. This pattern is called the usual arithmetic conversions, which are defined as follows:
— [..]
— Otherwise, the integral promotions (4.5) shall be performed on both operands.
其中积分提升在 4.5/1 中定义:
A prvalue of an integer type other than
bool
,char16_t
,char32_t
, orwchar_t
whose integer conversion rank (4.13) is less than the rank ofint
can be converted to a prvalue of typeint
ifint
can represent all the values of the source type; otherwise, the source prvalue can be converted to a prvalue of typeunsigned int
.
在我们的例子中,decltype(char + char)
是 int
,因为 char
的转化排名低于 int
,因此两者都被提升为 int
在调用 operator+
之前。现在,我们有 int
s,我们正在将其传递给采用 char
s 的构造函数。根据定义(§8.5.4/7,特别是 7.4):
A narrowing conversion is an implicit conversion
(7.4) — 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 whose value after integral promotions will fit into the target type.
根据 §8.5.4/3 明确禁止列表初始化(强调我的,“见下文”实际上指的是我刚刚在上面复制的内容):
List-initialization of an object or reference of type
T
is defined as follows— [..]
— 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. [...]
这就是您的 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)
{ }