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_t
but 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;
}
If you're going to compare an int type to size_t(or any other library type), you are doing a risky comparison because int is signed and size_t is unsigned, so one of them will be implicitly converted depending on your compiler/platform.
When writing C code you should always use size_t whenever dealing with memory ranges. The int type on the other hand is basically defined as the size of the (signed) integer value that the host machine can use to most efficiently perform integer arithmetic.
First, size_t normally means unsigned long , not int .
Just use casting. You can safely convert size_t to int by checking its range before casting it. If it is outside the valid int range, you can't. Test against INT_MAX to ensure no possible signed integer overflow before casting it.
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.
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.
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