警告:缩小转换范围 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-
函数中{...}
,警告就会消失。为什么?
首先,为什么要缩小范围?这来自§5/10:
许多期望算术或枚举类型的操作数的二元运算符以类似的方式导致转换并生成结果类型。目的是生成一个通用类型,这也是结果的类型。这种模式称为通常的算术转换,其定义如下:
— [..]
— 否则,应在两个操作数上执行积分提升 (4.5)。
在 4.5/1 中定义积分促销的情况下:
除
bool
、char16_t
、char32_t
或wchar_t
以外的整数类型的 prvalue 如果整数转换秩 (4.13) 小于int
秩,int
则可以将其转换为int
类型的 prvalue,如果该 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}
会给您一个警告:由于整数提升需要缩小所有表达式的转换,程序格式不正确。现在,关于"格式错误"的陈述仅在列表初始化的上下文中出现。这就是为什么如果您在没有 {}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 );
}
第二件事是,"嗯?夏亚减去一个夏尔就是夏尔,有什么问题?!这里的答案是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)
};
}
顺便说一下,有效的现代C++中的第 7 项让我相信,有时()
更适合初始化,有时{}
更好。有时你必须耸耸肩并使用另一个。
- C ++枚举范围无法使用-std=c ++ 98进行编译,但使用-std=c ++ 11可以
- C++11 基于范围的 for 循环,用于 std::list
- C++11:没有复制构造函数的自定义基于范围的循环
- 如何在C++11中区分填充构造函数和范围构造函数
- 自动和范围内 for 循环 C++11 不起作用
- 带钥匙的C 11地图定义了一个值范围
- 为什么即使在启用 C++11 并且我包含字符串之后,'stod'仍然没有在此范围内声明?
- C++11 迭代器和返回的 std::unique_ptr 的范围
- C++11:用容器参数定义函数(类似于基于范围的)
- 缩小 C++11 中的转换范围:"actual value after conversion"是什么?
- 范围/切片以 C++11/1y 为单位
- C 11矢量构造函数复制与范围
- C 11中的范围(故障)
- C 11中类范围的数组初始化
- 基于范围的循环:自动更改C 11中的含义
- GCC 4.4 不实现 C++11 范围循环.它还支持哪些其他范围循环语法
- 如何将基于迭代器的 for 循环重写为基于范围的循环 (C++11)
- 嵌套c++11范围循环,用于查找组合
- 更快的环路范围 (C++11)
- 警告:缩小转换范围 C++11