Converting int to a size_t

14.7k views Asked by At

I am wondering about the following warning of the clang compiler when I pass an integer to an std::initializer_list< size_t > :

non-constant-expression cannot be narrowed from type 'int' to 'unsigned long' in initializer list

Why can int be casted to a size_tbut an int not be passed to an std::initializer_list< size_t >, i.e.

int main()
{
  size_t s_t = 0;
  int    i   = 0;

  std::initializer_list<size_t> i_l = { i };            // warning
  s_t = i;                                              // no warning

  return 0;
}    
3

There are 3 answers

0
NathanOliver On BEST ANSWER

You have run afoul of [dcl.init.list]/7

A narrowing conversion is an implicit conversion[...] 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.

Since i is not a constant expression this counts as a narrowing conversion and narrowing conversions are not allowed in initializer list. If you were to use

std::initializer_list<std::size_t> i_l = { 0 };

Then it would not narrow even though 0 is an int since the compiler knows 0 can be represented in each type.

0
R Sahu On

When a narrowing conversion is required while using list initialization to initialize an object, the program is ill-formed.

From the C++11 Standard (emphasis mine):

8.5.4 List-initialization

...

3 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.

The use of

int i = 0;
size_t s = {i};

falls under the same clause.

0
Barry On

From [dcl.init.list]:

A narrowing conversion is an implicit conversion [...] — 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.

We are converting from int (which allows for negative values) to size_t (which does not), so this is a narrowing conversion. Narrowing conversions are ill-formed in list-initialization, which is what you're doing here:

std::initializer_list<size_t> i_l = { i };

However, narrowing conversions are fine elsewhere (as far as the standard is concerned):

s_t = i;

That's why the first line is ill-formed but the second is not.