An issue with signedness of char

问题描述 投票:0回答: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?

c++ vector char int list-initialization
1个回答
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.

© www.soinside.com 2019 - 2024. All rights reserved.