[英]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.