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?
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.
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 .
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.
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
, 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
.
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 int
s that we're passing to a constructor that takes char
s. 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) { }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With