Warning: Narrowing Conversion C++11

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

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 type int, 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



Leave a reply



Submit