Is std::is_unsigned<bool>::value well defined?
C++BooleanLanguage LawyerC++14UnsignedC++ Problem Overview
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.
C++ Solutions
Solution 1 - C++
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
.
Solution 2 - C++
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 bool
ean 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.
Solution 3 - C++
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.
Solution 4 - C++
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.