Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

warning: narrowing conversion C++11

Tags:

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?

like image 593
hidayat Avatar asked Jan 08 '15 16:01

hidayat


People also ask

How do you fix narrowing conversion error?

If you make a narrowing conversion intentionally, make your intentions explicit by using a static cast. Otherwise, this error message almost always indicates you have a bug in your code. You can fix it by making sure the objects you initialize have types that are large enough to handle the inputs.

What is a narrowing conversion?

A narrowing conversion changes a value to a data type that might not be able to hold some of the possible values. For example, a fractional value is rounded when it is converted to an integral type, and a numeric type being converted to Boolean is reduced to either True or False .

Does Java allow narrowing?

Core Java bootcamp program with Hands on practiceNarrowing conversion is needed when you convert from a larger size type to a smaller size. This is for incompatible data types, wherein automatic conversions cannot be done. Let us see an example wherein we are converting long to integer using Narrowing Conversion.


1 Answers

First, why narrowing? That comes from §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.

where the integral promotion is defined in 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.

In our case then, we have decltype(char + char) is int because char's conversion rank less than int so both are promoted to int before 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):

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.

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):

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

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> 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) { }  
like image 85
Barry Avatar answered Sep 20 '22 13:09

Barry