-1

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:

  1. 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?

  2. If my deduction of the problem is wrong, what is the correct reason behind this issue?

Ardent Coder
  • 3,777
  • 9
  • 27
  • 53
  • Comments are not for extended discussion; this conversation has been [moved to chat](https://chat.stackoverflow.com/rooms/213289/discussion-on-question-by-ardent-coder-narrowing-conversion-from-int-to-char-ins). – Samuel Liew May 07 '20 at 01:27

1 Answers1

3

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.

Ardent Coder
  • 3,777
  • 9
  • 27
  • 53
M.M
  • 138,810
  • 21
  • 208
  • 365
  • 1
    replace `which is out of range of char` with `which is out of range of for char on your system`? – NathanOliver May 06 '20 at 14:45
  • `'r' + 's'` is `236`. It could be out of `char`'s range as you said, but only if it was `signed char`. Then `vector` should also have been `vector`. But in my case `vector` was indeed `vector` but the *r + s* `char` was `unsigned char`. – Ardent Coder May 06 '20 at 14:45
  • 1
    @ArdentCoder `char` and `signed char` are different types, but have the same properties on your system – M.M May 06 '20 at 14:48
  • I'm not talking about `123456` which is clearly out of range. I was dealing with `236` in my code which is well within the range of `unsigned char`. But as you said it's compiler-dependent, my compiler saw it like `signed char` and I agree. Then why did the other `char` in my code was not seen like signed, because it compiled and displayed the infinity symbol as expected. – Ardent Coder May 06 '20 at 15:44
  • 1
    @ArdentCoder -- in case it isn't clear: the signedness of `char` is determined by the compiler and the compiler uses the **same** signedness throughout the compilation. It does not make case-by-case decisions while compiling a single source file. Some compilers give you a command-line switch that lets you choose which one to use, but, again, that applies throughout the compilation. – Pete Becker May 06 '20 at 16:02
  • Oh yes, I couldn't digest the *out of range conversion* discussion in the comments because my power went off and I was testing your explanations in an online compiler from a mobile device which didn't display `∞` lol – Ardent Coder May 07 '20 at 05:54