warning: narrowing conversion C++11
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)
{ }
Narrowing conversion error using newer compiler
Why does this issue suddenly appear, without specifying a new C++ version?
Newer releases/versions of the 'same' compiler quite often have stricter requirements (in terms of conformance to the C++ Standard) than earlier/older ones. This appears to be so in your case.
Where does the number '\37777777600' come from?
This is the value of the '\x80'
literal (-128
) expressed as a 32-bit integer in octal. The value is sign-extended to an int
(32-bits on your system) because of the following rule (quoted from this Draft C11 Standard):
5.13.3 Character literals
…
…
2 … A multicharacter literal, or an ordinary
character literal containing a single c-char not representable in the
execution character set, is conditionally-supported, has typeint
, and
has an implementation-defined value.
It would appear that -128
is not a 'representable character' in your platform's execution set. However, why the g++ compiler chooses to use octal is not something I can answer in any authoritative manner. (Tradition, maybe?)
I'd prefer not to change the library - is there another way around this?
There may be a command-line compiler switch that reduces the compiler's strictness; MSVC has the /permissive
flag to turn off "Conformance Mode" but I'm not sure what the g++ equivalent is (or even if there is one). Failing that, a fairly trivial change to your code, to remove the use of character literals for unsigned char
data (which are, anyway, expressed as raw hex values) would work:
int main()
{
static const unsigned char pad_block[8] = { 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
//...
}
Why is there a narrowing conversion warning from int to short when adding shorts? (C++)
This happens because of integer promotion. The result of adding two short
values is not short
, but int
.
You can check this with cppinsights.io:
short a = 1;
short b = 2;
auto c = a + b; // c is int
Demo: https://cppinsights.io/s/68e27bd7
C++11: narrowing conversion inside { } with modulus
In this specific case making id
const or constexpr will fix the problem:
constexpr unsigned int id = 100;
since there is an exception for the case where you have a constant expression whose result after conversion will fit into the target type.
In the more general case you may also use static_cast to cast the result to unsigned char:
{ static_cast<unsigned char>( id % 3), static_cast<unsigned char>( id % 5) }
^^^^^^^^^^^ ^^^^^^^^^^^
We can find he exception for constant expressions and narrowing conversions in the draft C++ standard section 8.5.4
List-initialization which says:
A narrowing conversion is an implicit conversion
and include the following bullet (emphasis mine):
- 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.
Note, the wording changed from the original draft C++11 standard to what I quote above due to defect report 1449.
Why is this narrowing conversion not detected?
Yes. You are right: the program is ill-formed.
In such a case (standard §1.4):
a conforming implementation shall issue at least one diagnostic message.
Indeed, gcc
produces a warning message. clang
directly rejects the code as a compiler error.
This specific topic has already been discussed here for gcc1.
Visual Studio
is supposed to produce a diagnostic message (I suggest you check your compilation options. Did you disable warnings? Are you compiling with C++(11/14/17)?, ...). If this is not the case, it's an implementation bug.
Update:
Visual Studio v19.20 does not produce any diagnostic message (even with /Wall
flag).
A bug report has been filled here.
1 For additional information regarding gcc implementation for narrowing check here.
Why gcc warns about narrowing conversion only for uniform initialization?
Because the standard says, narrowing conversions limit is specified only for list initialization (since C++11).
list-initialization limits the allowed implicit conversions by
prohibiting the following:
- conversion from a floating-point type to an integer type
- conversion from a long double to double or to float and conversion from double to float, except where the source is a constant expression
and overflow does not occur- conversion from an integer type to a floating-point type, except where the source is a constant expression whose value can be stored
exactly in the target type- conversion from integer or unscoped enumeration type to integer type that cannot represent all values of the original, except where source
is a constant expression whose value can be stored exactly in the
target type
For the other initialization methods (using parentheses or equal sign), narrowing conversions limit rule is not applied (added); because that might break much legacy code.
Force narrowing conversion warning
You can trigger a narrowing conversion warning with uniform initialization syntax:
class wrapper
{
template <class> friend class wrapper;
public:
constexpr wrapper(T value)
: _data{value}
{}
template <class U>
constexpr wrapper(wrapper<U> other)
: _data{other._data} // note the curly brackets here
{}
wrapper& operator=(T value)
{_data = value; return *this;}
template <class U>
wrapper& operator=(wrapper<U> other)
{_data = {other._data}; return *this;} // and here
private:
T _data;
};
with
wrapper<unsigned int> wrapper1 = 5U;
wrapper<unsigned char> wrapper2 = wrapper1; // Narrowing
wrapper<unsigned char> wrapper3(wrapper1); // Narrowing
wrapper<unsigned char> wrapper4{wrapper1}; // Narrowing
wrapper2 = wrapper1; // Narrowing
any of the four last lines will produce a narrowing conversion warning in g++, and compilation errors from the narrowing conversions in clang.
Cast and Fail Compilation if a narrowing conversion is possible
Initialization with braces (but not parentheses) disallows narrowing conversions:
int32_t r{a};
// or
int32_t r = {a};
// or
auto r = int32_t{a};
Your compiler may be allowing this anyway, but that is not standard-conform [1]. E.g. for GCC you need to add the -pedantic-errors
flag for it to actually generate a hard error.
Also note that the type for %d
should be int
. If you use int32_t
instead, you are risking a similar issue should a platform use a differently sized int
.
You can use it directly in the printf
call:
printf("%d", int{a});
[1] The standard always only requires compilers to print some diagnostic. It does not require hard errors preventing the compilation of the program. GCC for example only warns by-default, but that is still conforming.
Relation between static constant member variables and narrowing conversions in C++
Why does this happen?
Because list initialization (since C++11) prohibits narrowing conversions.
(emphasis mine)
- conversion from integer or unscoped enumeration type to integer type that cannot represent all values of the original, except where source is a constant expression whose value can be stored exactly in the target type
If you declare var
as static const/constexpr
, it becomes a constant expression and the value 92
could be stored in char
, then the code works fine.
Related Topics
Copy Constructor of Derived Qt Class
Static Initialization and Destruction of a Static Library's Globals Not Happening with G++
G++ Ld: Symbol(S) Not Found for Architecture X86_64
C++ Unions VS. Reinterpret_Cast
Warning: Narrowing Conversion C++11
Qt Undefined Reference to Vtable
What's the Real Use of Using N[C-'0']
Trouble with Dependent Types in Templates
Why Do You Need to Append an L or F After a Value Assigned to a C++ Constant
What's the Difference Between C and C++
Read Qprocess Output to String
Unaligned Access Through Reinterpret_Cast
For-Loop in C++ Using Double Breaking Out One Step Early, Boundary Value Not Reached
Does Insertion to Stl Map Invalidate Other Existing Iterator