See the behaviour of char
in case of a vector<char>
and a standalone char
:
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> someInts = {2, 3, 1 + 5};
// I can perform operations ^^^^^ inside the braces in this case
// vector<char> someChars = {'a', 'b', 'r' + 'z'};
// But doing the same in case of char ^^^^^^^^^ gives an error
// However, it works for a standalone char
char ch = 'r' + 'z';
cout << ch;
// Value^^^is '∞' as expected
}
Uncommenting the vector<char>
line gives:
Error: narrowing conversion of '236' from 'int' to 'char' inside { }
This was the problem.
Then I read this documentation of List Initialization, and saw the following which might be related to this problem:
Narrowing conversions
list-initialization limits the allowed implicit conversions by prohibiting the following:
- many other reasons
- 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
It made me understand the error message (I guess), and I changed vector<char>
to vector<unsigned char>
and it worked:
vector<unsigned char> someChars = {'a', 'b', 'r' + 'z'};
for (char ch : someChars)
cout << '_' << ch;
Output:
_a_b_∞
So, my question is:
If signedness of char
was the problem, how did the standalone version of char
work in that case? With reference to this thread on stackoverflow, how did the same compiler choose signed char
for vector<char>
and unsigned char
for char
?
If my deduction of the problem is wrong, what is the correct reason behind this issue?
1+5
is an int
so there is no problem using it to initialize a vector<int>
entry.
'r' + 's'
is an int
(236) which is out of range of char
on your system. So there is a problem trying to use it to initialize a vector<char>
entry. This is exactly the sort of case that the rule about list initialization disallowing narrowing conversions was designed for.
You would get the same error for vector<char> x = { 'a', 'b', 123456 };
Standalone char
either has the same properties as signed char
or unsigned char
, which one it is is implementation-defined and some compilers have a switch to choose (e.g. -funsigned-char
on GCC). To be clear, it is still a distinct type in either case.
In your words, char
was indeed signed in both the cases as Nathan Oliver explained in the comments. The problem is that you have a wrong notion that the standalone char
was unsigned in your case. Just because char ch = 'r' + 'z';
compiled doesn't show that it is unsigned. You probably thought that it was unsigned because 'r' + 'z' == 236
which doesn't fit into a signed char
. No, it does fit into a signed char
for the following reason:
char ch = 'r' + 'z';
is an out-of-range conversion. Since C++20 the result will be -20
(assuming plain char is 8-bit signed as indicated by your earlier error message, and the system uses ASCII encoding) , prior to that the result was implementation-defined.
So, don't assume things. If you are confused whether char is signed or unsigned on your system, simply check CHAR_MIN
.