警告:缩小转换 C++11

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/27844971/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 20:52:54  来源:igfitidea点击:

warning: narrowing conversion C++11

c++templatesc++11narrowing

提问by hidayat

g++ 4.9.0 -O2 -std=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;
}

I get the warning :

我收到警告:

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

But if I do it with (...)insted of {...}inside the operator-function the warning disappears. Why?

但是,如果我在函数内部使用(...)insted执行此操作,警告就会消失。为什么?{...}operator-

采纳答案by Barry

First, why narrowing? That comes from §5/10:

首先,为什么要缩小?这来自§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)。

where the integral promotion is defined in 4.5/1:

其中积分提升在 4.5/1 中定义:

A prvalue of an integer type other than bool, char16_t, char32_t, or wchar_twhose integer conversion rank (4.13) is less than the rank of intcan be converted to a prvalue of type intif intcan represent all the values of the source type; otherwise, the source prvalue can be converted to a prvalue of type unsigned int.

bool, char16_t,以外的整数类型的纯右值char32_t,或其wchar_t整数转换等级 (4.13) 小于 的等级,如果可以表示源类型的所有值,则可以int转换为类型的纯右值;否则,可以将源纯右值转换为 类型的纯右值。intintunsigned int

In our case then, we have decltype(char + char)is intbecause char's conversion rank less than intso both are promoted to intbefore the call to operator+. Now, we have ints that we're passing to a constructor that takes chars. By definition (§8.5.4/7, specifically 7.4):

在我们的例子中,我们有decltype(char + char)int因为char的转换等级低于int所以两者都int在调用之前被提升到operator+。现在,我们将ints 传递给一个接受chars的构造函数。根据定义(第 8.5.4/7 节,特别是第 7.4 节):

A narrowing conversionis 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.

缩小转换是隐式转换

(7.4) — 从整数类型或无作用域枚举类型到不能表示原始类型所有值的整数类型,除非源是常量表达式,其整数提升后的值将适合目标类型。

which is explicitly prohibited in list-initialization specifically as per §8.5.4/3 (emphasis mine, the "see below" actually refers to what I just copied above):

根据 §8.5.4/3 在列表初始化中明确禁止(强调我的,“见下文”实际上是指我刚刚在上面复制的内容):

List-initialization of an object or reference of type Tis defined as follows

— [..]

— Otherwise, if Tis 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. [...]

对象或类型引用的列表初始化T定义如下

— [..]

— 否则,如果T是类类型,则考虑构造函数。枚举适用的构造函数,并通过重载决议(13.3、13.3.1.7)选择最佳构造函数。如果需要缩小转换(见下文)来转换任何参数,则程序格式错误。[...]

This is why your vec3<T>{int, int, int}gives you a warning: the program is ill-formed due to integer promotion requiring a narrowing conversion on all the expressions. Now, the statement about "ill-formed" specifically arises only in the context of list-initialization. This is why if you initialize your vector without {}s, you do not see that warning:

这就是为什么你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 );
}

As to solving this problem - just calling the constructor without list-initialization is probably the simplest solution. Alternatively, you can continue to use list-initialization and just template your constructor:

至于解决这个问题——只调用没有列表初始化的构造函数可能是最简单的解决方案。或者,您可以继续使用列表初始化并仅模板您的构造函数:

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)
{ } 

回答by Darren Cook

A couple of things are going on here. First, the {...}syntax prohibits implicit narrowing conversions. So the easy fix is to change curly brackets to parentheses:

这里正在发生一些事情。首先,{...}语法禁止隐式收缩转换。所以简单的解决方法是将大括号更改为圆括号:

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

The second thing going on is, "eh? char minus a char is a char, what is the problem?!" And the answer here is that C/C++ want to use natural sizefor arithmetic operations. That is why you see the (int)cast in your error message. Here is a good explanationof why it does that (just in case that StackOverflow answer ever disappears, he's quoting 6.3.1.1 of the C11 standard).

发生的第二件事是,“嗯?字符减去一个字符就是一个字符,有什么问题?!” 这里的答案是 C/C++ 想要使用自然大小进行算术运算。这就是为什么您会(int)在错误消息中看到演员表。这是为什么这样做的一个很好的解释(以防万一 StackOverflow 答案消失了,他引用了 C11 标准的 6.3.1.1)。

So, the other way to fix your code is:

因此,修复代码的另一种方法是:

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)
    };
}

By the way, item 7 in Effective Modern C++ convinced me that there are times when ()is better to initialize with, and there are times when {}is better. Sometimes you have to just shrug and use the other one.

顺便说一下,Effective Modern C++ 中的第 7 项让我相信,有时()初始化更好,有时{}也更好。有时你不得不耸耸肩并使用另一个。