I am wondering whether
std::is_unsigned<bool>::value
is well defined according to the standard or not?
I ask the question because typename std::make_unsigned<bool>::type is not well defined.
There is no concept of signedness for bool. From [basic.fundamental]/6:
Values of type bool are either true of false. [Note: There are no signed, unsigned, short, or long bool types or values. — end note] Values of type bool participate in integral promotions (4.5).
By contrast, signedness is explicitly called out for the signed integer types (paragraph 2) and unsigned integer types (paragraph 3).
Now for the is_signed and is_unsigned traits. First off, the traits are always well-defined, but only interesting for arithmetic types. bool is an arithmetic type, and is_signed<T>::value is defined (see Table 49) as T(-1) < T(0). By using the rules of boolean conversion and standard arithmetic conversions, we see that this is is false for T = bool (because bool(-1) is true, which converts to 1). Similarly, is_unsigned<T>::value is defined as T(0) < T(-1), which is true for T = bool.
is_unsigned is defined in [meta.unary.comp]/2 as
If is_arithmetic<T>::value is true, the same result as
bool_constant<T(0) < T(-1)>::value; otherwise, false
bool† is clearly an arithmetic type (being integral). Now consider [conv.bool]/1:
A zero value, null pointer value, or null member pointer value is converted to false; any other value is converted to true.
I.e. bool(0) < bool(-1) is equivalent to false < true, and the latter holds since the values are promoted to 0 and 1, respectively.
Thus is_unsigned<bool>::value is true (and, conversely, is_signed is false), due to the fact that boolean values correspond to the unsigned values 0 and 1 during arithmetic operations. However, it doesn't really make sense to assess bool's signedness, much less perform make_unsigned on it, since it doesn't represent integers, but rather states.
†: The fact that this template is applicable to bool in the first place is determined by its Requirement clause being non-existent, bool not being an incomplete type ([res.on.functions]/(2.5)) and no other requirements being mentioned in [meta.rqmts] for UnaryTypeTraits.
Yes, it is well-defined, as is any other unary type trait.
C++14 (n4140) 20.10.4/2 "Unary type traits" mandates:
Each of these templates shall be a UnaryTypeTrait (20.10.1) with a BaseCharacteristic of true_type if the
corresponding condition is true, otherwise false_type.
20.10.1/1:
A UnaryTypeTrait describes a property of a type. It shall be a class template that takes one template type
argument and, optionally, additional arguments that help define the property being described. It shall be
DefaultConstructible, CopyConstructible, and publicly and unambiguously derived, directly or indirectly,
from its BaseCharacteristic, which is a specialization of the template integral_constant (20.10.3),
with the arguments to the template integral_constant determined by the requirements for the particular
property being described. The member names of the BaseCharacteristic shall not be hidden and shall be
unambiguously available in the UnaryTypeTrait.
From this it follows that the construct std::is_unsigned<T>::value has to be well-defined for any type T, whether the concept of "signedness" makes sense for the type or not.
Yes it is well defined and the result should be std::is_unsigned<bool>::value == true
The documentation for std::is_signed says
If T is a signed arithmetic type, provides the member constant value equal true. For any other type, value is false.
So then if you look at std::is_arithmetic
If T is an arithmetic type (that is, an integral type or a floating-point type), provides the member constant value equal true. For any other type, value is false.
Which finally leads to std::is_integral
Checks whether T is an integral type. Provides the member constant value which is equal to true, if T is the type bool, char, char16_t, char32_t, wchar_t, short, int, long, long long, or any implementation-defined extended integer types, including any signed, unsigned, and cv-qualified variants. Otherwise, value is equal to false.
Interestingly, there is another function std::numeric_limits::is_signed that states
The value of std::numeric_limits<T>::is_signed is true for all signed arithmetic types T and false for the unsigned types. This constant is meaningful for all specializations.
Where the specialization for bool is listed as false, which also confirms that bool is considered unsigned.
Related
Is it just the reinterpret_cast?
int *pointer;
uintptr_t value;
value == reinterpret_cast<uintptr_t>(pointer);
Depends on your goal really.
[expr.reinterpret.cast]
4 A pointer can be explicitly converted to any integral type
large enough to hold it. The mapping function is
implementation-defined. [ Note: It is intended to be unsurprising to
those who know the addressing structure of the underlying machine.
— end note ] A value of type std::nullptr_t can be converted to an
integral type; the conversion has the same meaning and validity as a
conversion of (void*)0 to the integral type.
5 A value of integral type or enumeration type can be explicitly
converted to a pointer. A pointer converted to an integer of
sufficient size (if any such exists on the implementation) and back to
the same pointer type will have its original value; mappings between
pointers and integers are otherwise implementation-defined.
The mapping is implementation defined (obviously). If you wish to check that the value of pointer was used to initialize value, then your check is insufficient. The above doesn't promise that reinterpret_cast<uintptr_t>(pointer) will always yield the same integer, even though all sane implementations today do.
I would do the check in reverse, since we have a round trip guarantee:
reinterpret_cast<int*>(value) == pointer;
But even then, it's a pretty weak guarantee. I would not faff about with these conversions too much if I were you. It may be worth to reconsider your design.
If you follow the standard to the letter, you ought to use
value == (uintptr_t)(void*)pointer
or using reinterpret_cast:
value == reinterpret_cast<uintptr_t>(reinterpret_cast<void*>(pointer))
which personally I find less readable. Naturally the compiler will remove all the "fluff".
When using comparison operators in C++, are bools converted to ints?
The reason I ask is that the question of whether or not to always explicitly compare to true/false in if-statements came up. The two options being:
1) if (my_bool == true) doSomething();
2) if (my_bool) doSomething();
We were thinking you should generally avoid the explicit comparison (1) because of the following:
int myFunc(){return 4;}
if (myFunc() == true) doSomething();
Something like the code above would come up if you need to work with C interfaces that simply return nonzero to indicate "true". The myFunc() example would fail in C because myFunc returns 4, true is macro'd to 1, and 4 == 1 is not true.
Is this still the case in C++? Does the "equal to" operator convert the bool to an int rather than the other way around? References to the standard (C++11 is what I'm working with) are appreciated, but if it differs among versions of the language I'd be interested in knowing.
(I'd like to ask specifically about the pros/cons of explicit true/false comparison, but that seems like it could be subjective.)
When using comparison operators in C++, are bools converted to ints?
Yes. For relational operators ([expr.rel]/5):
The usual arithmetic conversions are performed on operands of arithmetic or enumeration type.
For equality operators ([expr.eq]/6):
If both operands are of arithmetic or enumeration type, the usual arithmetic conversions are performed on
both operands;
When both operands are bool, they are both promoted to int. If one operand is a bool and the other is of integral type, the bool operand is promoted to int. See [expr]/10:
Otherwise, the integral promotions (4.5) shall be performed on both operands.
As far as I know, this has been true since the beginning of time (it does not differ between revisions of the standard).
I do not think there is any performance implication of doing explicit comparison to true or false, but I wouldn't do it myself, since I consider it redundant and not in a way that yields any benefit.
They are not converted, bool actually ARE integer types.
See the standard:
"Values of type bool are either true or false. As described below, bool values behave as integral types. Values of type bool participate in integral promotions" ~ C++03
Consider this C++ code:
enum class Color : char { red = 0x1, yellow = 0x2 }
// ...
char *data = ReadFile();
Color color = static_cast<Color>(data[0]);
Suppose that data[0] is actually 100. What is color set to according to the standard?
In particular, if I later do
switch (color) {
// ... red and yellow cases omitted
default:
// handle error
break;
}
does the standard guarantee that default will be hit? If not, what is the proper, most efficient, most elegant way to check for an error here? Does the standard make any guarantees as about this but with plain enum?
What is color set to according to the standard?
Answering with a quote from the C++11 and C++14 Standards:
[expr.static.cast]/10
A value of integral or enumeration type can be explicitly converted to an enumeration type. The value is unchanged if the original value is within the range of the enumeration values (7.2). Otherwise, the resulting value is unspecified (and might not be in that range).
Let's look up the range of the enumeration values: [dcl.enum]/7
For an enumeration whose underlying type is fixed, the values of the enumeration are the values of the underlying type.
Before CWG 1766 (C++11, C++14)
Therefore, for data[0] == 100, the resulting value is specified(*), and no Undefined Behaviour (UB) is involved. More generally, as you cast from the underlying type to the enumeration type, no value in data[0] can lead to UB for the static_cast.
After CWG 1766 (C++17)
See CWG defect 1766.
The [expr.static.cast]p10 paragraph has been strengthened, so you now can invoke UB if you cast a value that is outside the representable range of an enum to the enum type. This still doesn't apply to the scenario in the question, since data[0] is of the underlying type of the enumeration (see above).
Please note that CWG 1766 is considered a defect in the Standard, hence it is accepted for compiler implementers to apply to to their C++11 and C++14 compilation modes.
(*) char is required to be at least 8 bit wide, but isn't required to be unsigned. The maximum value storable is required to be at least 127 per Annex E of the C99 Standard.
Compare to [expr]/4
If during the evaluation of an expression, the result is not mathematically defined or not in the range of representable values for its type, the behavior is undefined.
Before CWG 1766, the conversion integral type -> enumeration type can produce an unspecified value. The question is: Can an unspecified value be outside the representable values for its type? I believe the answer is no -- if the answer was yes, there wouldn't be any difference in the guarantees you get for operations on signed types between "this operation produces an unspecified value" and "this operation has undefined behaviour".
Hence, prior to CWG 1766, even static_cast<Color>(10000) would not invoke UB; but after CWG 1766, it does invoke UB.
Now, the switch statement:
[stmt.switch]/2
The condition shall be of integral type, enumeration type, or class type. [...] Integral promotions are performed.
[conv.prom]/4
A prvalue of an unscoped enumeration type whose underlying type is fixed (7.2) can be converted to a prvalue of its underlying type. Moreover, if integral promotion can be applied to its underlying type, a prvalue of an unscoped enumeration type whose underlying type is fixed can also be converted to a prvalue of the promoted underlying type.
Note: The underlying type of a scoped enum w/o enum-base is int. For unscoped enums the underlying type is implementation-defined, but shall not be larger than int if int can contain the values of all enumerators.
For an unscoped enumeration, this leads us to /1
A prvalue of an integer type other than bool, char16_t, char32_t, or wchar_t whose integer conversion rank (4.13) is less than the rank of int can be converted to a prvalue of type int if int can represent all the values of the source type; otherwise, the source prvalue can be converted to a prvalue of type unsigned int.
In the case of an unscoped enumeration, we would be dealing with ints here. For scoped enumerations (enum class and enum struct), no integral promotion applies. In any way, the integral promotion doesn't lead to UB either, as the stored value is in the range of the underlying type and in the range of int.
[stmt.switch]/5
When the switch statement is executed, its condition is evaluated and compared with each case constant. If one of the case constants is equal to the value of the condition, control is passed to the statement following the matched case label. If no case constant matches the condition, and if there is a default label, control passes to the statement labeled by the default label.
The default label should be hit.
Note: One could take another look at the comparison operator, but it is not explicitly used in the referred "comparison". In fact, there's no hint it would introduce UB for scoped or unscoped enums in our case.
As a bonus, does the standard make any guarantees as about this but with plain enum?
Whether or not the enum is scoped doesn't make any difference here. However, it does make a difference whether or not the underlying type is fixed. The complete [decl.enum]/7 is:
For an enumeration whose underlying type is fixed, the values of the enumeration are the values of the underlying type. Otherwise, for an enumeration where emin is the smallest enumerator and emax is the largest, the values of the enumeration are the values in the range bmin to bmax, defined as follows: Let K be 1 for a two's complement representation and 0 for a one's complement or sign-magnitude representation. bmax is the smallest value greater than or equal to max(|emin| − K, |emax|) and equal to 2M − 1, where M is a non-negative integer. bmin is zero if emin is non-negative and −(bmax + K) otherwise.
Let's have a look at the following enumeration:
enum ColorUnfixed /* no fixed underlying type */
{
red = 0x1,
yellow = 0x2
}
Note that we cannot define this as a scoped enum, since all scoped enums have fixed underlying types.
Fortunately, ColorUnfixed's smallest enumerator is red = 0x1, so max(|emin| − K, |emax|) is equal to |emax| in any case, which is yellow = 0x2. The smallest value greater or equal to 2, which is equal to 2M - 1 for a positive integer M is 3 (22 - 1). (I think the intent is to allow the range to extent in 1-bit-steps.) It follows that bmax is 3 and bmin is 0.
Therefore, 100 would be outside the range of ColorUnfixed, and the static_cast would produce an unspecified value before CWG 1766 and undefined behaviour after CWG 1766.
Consider this C++ code:
enum class Color : char { red = 0x1, yellow = 0x2 }
// ...
char *data = ReadFile();
Color color = static_cast<Color>(data[0]);
Suppose that data[0] is actually 100. What is color set to according to the standard?
In particular, if I later do
switch (color) {
// ... red and yellow cases omitted
default:
// handle error
break;
}
does the standard guarantee that default will be hit? If not, what is the proper, most efficient, most elegant way to check for an error here? Does the standard make any guarantees as about this but with plain enum?
What is color set to according to the standard?
Answering with a quote from the C++11 and C++14 Standards:
[expr.static.cast]/10
A value of integral or enumeration type can be explicitly converted to an enumeration type. The value is unchanged if the original value is within the range of the enumeration values (7.2). Otherwise, the resulting value is unspecified (and might not be in that range).
Let's look up the range of the enumeration values: [dcl.enum]/7
For an enumeration whose underlying type is fixed, the values of the enumeration are the values of the underlying type.
Before CWG 1766 (C++11, C++14)
Therefore, for data[0] == 100, the resulting value is specified(*), and no Undefined Behaviour (UB) is involved. More generally, as you cast from the underlying type to the enumeration type, no value in data[0] can lead to UB for the static_cast.
After CWG 1766 (C++17)
See CWG defect 1766.
The [expr.static.cast]p10 paragraph has been strengthened, so you now can invoke UB if you cast a value that is outside the representable range of an enum to the enum type. This still doesn't apply to the scenario in the question, since data[0] is of the underlying type of the enumeration (see above).
Please note that CWG 1766 is considered a defect in the Standard, hence it is accepted for compiler implementers to apply to to their C++11 and C++14 compilation modes.
(*) char is required to be at least 8 bit wide, but isn't required to be unsigned. The maximum value storable is required to be at least 127 per Annex E of the C99 Standard.
Compare to [expr]/4
If during the evaluation of an expression, the result is not mathematically defined or not in the range of representable values for its type, the behavior is undefined.
Before CWG 1766, the conversion integral type -> enumeration type can produce an unspecified value. The question is: Can an unspecified value be outside the representable values for its type? I believe the answer is no -- if the answer was yes, there wouldn't be any difference in the guarantees you get for operations on signed types between "this operation produces an unspecified value" and "this operation has undefined behaviour".
Hence, prior to CWG 1766, even static_cast<Color>(10000) would not invoke UB; but after CWG 1766, it does invoke UB.
Now, the switch statement:
[stmt.switch]/2
The condition shall be of integral type, enumeration type, or class type. [...] Integral promotions are performed.
[conv.prom]/4
A prvalue of an unscoped enumeration type whose underlying type is fixed (7.2) can be converted to a prvalue of its underlying type. Moreover, if integral promotion can be applied to its underlying type, a prvalue of an unscoped enumeration type whose underlying type is fixed can also be converted to a prvalue of the promoted underlying type.
Note: The underlying type of a scoped enum w/o enum-base is int. For unscoped enums the underlying type is implementation-defined, but shall not be larger than int if int can contain the values of all enumerators.
For an unscoped enumeration, this leads us to /1
A prvalue of an integer type other than bool, char16_t, char32_t, or wchar_t whose integer conversion rank (4.13) is less than the rank of int can be converted to a prvalue of type int if int can represent all the values of the source type; otherwise, the source prvalue can be converted to a prvalue of type unsigned int.
In the case of an unscoped enumeration, we would be dealing with ints here. For scoped enumerations (enum class and enum struct), no integral promotion applies. In any way, the integral promotion doesn't lead to UB either, as the stored value is in the range of the underlying type and in the range of int.
[stmt.switch]/5
When the switch statement is executed, its condition is evaluated and compared with each case constant. If one of the case constants is equal to the value of the condition, control is passed to the statement following the matched case label. If no case constant matches the condition, and if there is a default label, control passes to the statement labeled by the default label.
The default label should be hit.
Note: One could take another look at the comparison operator, but it is not explicitly used in the referred "comparison". In fact, there's no hint it would introduce UB for scoped or unscoped enums in our case.
As a bonus, does the standard make any guarantees as about this but with plain enum?
Whether or not the enum is scoped doesn't make any difference here. However, it does make a difference whether or not the underlying type is fixed. The complete [decl.enum]/7 is:
For an enumeration whose underlying type is fixed, the values of the enumeration are the values of the underlying type. Otherwise, for an enumeration where emin is the smallest enumerator and emax is the largest, the values of the enumeration are the values in the range bmin to bmax, defined as follows: Let K be 1 for a two's complement representation and 0 for a one's complement or sign-magnitude representation. bmax is the smallest value greater than or equal to max(|emin| − K, |emax|) and equal to 2M − 1, where M is a non-negative integer. bmin is zero if emin is non-negative and −(bmax + K) otherwise.
Let's have a look at the following enumeration:
enum ColorUnfixed /* no fixed underlying type */
{
red = 0x1,
yellow = 0x2
}
Note that we cannot define this as a scoped enum, since all scoped enums have fixed underlying types.
Fortunately, ColorUnfixed's smallest enumerator is red = 0x1, so max(|emin| − K, |emax|) is equal to |emax| in any case, which is yellow = 0x2. The smallest value greater or equal to 2, which is equal to 2M - 1 for a positive integer M is 3 (22 - 1). (I think the intent is to allow the range to extent in 1-bit-steps.) It follows that bmax is 3 and bmin is 0.
Therefore, 100 would be outside the range of ColorUnfixed, and the static_cast would produce an unspecified value before CWG 1766 and undefined behaviour after CWG 1766.
Not only is this valid and doesn't give any warnings even with -Wall:
void* p = false; // actually 'true' doesn't work here
bool b = "Hello, Boolean!";
but also this compatibility rule permits selecting an overloaded function/operator for a wrong type. Let's say you overloaded your operator << for all fundamental types and you forgot to overload the void pointer, then the compiler may select the version that takes bool, or the other way around.
So what is it that makes this compatibility rule more important than the weird (and highly undesirable) side effects with overloaded functions?
(Edit: removed all references to C, they were wrong: the conversion rules are basically the same in C.)
What do you mean by "C can handle this correctly"? C doesn't permit function overloading, so you are guaranteed to have the bool <-> pointer conversion you're complaining about.
Are you asking why this conversion exists?
The first is not actually a conversion bool -> pointer, but is recognizing that the literal false means 0, which is a valid pointer value. That's why it doesn't work with true, and it doesn't work with a bool variable.
The second is because it's nice to be able to write:
if (p)
instead of
if (p != 0)
to check if a pointer holds a null pointer value.
EDIT: Rules from the standard influencing T* p = false;:
A null pointer constant is an integral constant expression prvalue of integer type that evaluates to zero
and
Types bool, char, char16_t, char32_t, wchar_t, and the signed and unsigned integer types are collectively called integral types. A synonym for integral type is integer type.
and
The Boolean literals are the keywords false and true. Such literals are prvalues and have type bool.