58

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.

Columbo
  • 60,038
  • 8
  • 155
  • 203
Vincent
  • 57,703
  • 61
  • 205
  • 388

4 Answers4

66

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.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
24

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.
Columbo
  • 60,038
  • 8
  • 155
  • 203
  • Slight nitpick: "values are promoted to 0 and 1, respectively" is not 100% correct. The conversion rules (4.12) say that a zero integer value converts to `false` and _any other_ value converts to `true` (no 1 involved anywhere). The standard also defines a conversion back to integer where `true` will _convert_ to 1, and `false` will _convert_ to 0, it does however not say anything about what the values actually _are_. For all we know, `true` could be 67 and `false` could be 172 (that's almost certainly never the case, but according to the standard, it could be). Still +1. – Damon Jan 05 '16 at 13:35
  • 2
    @Damon Wait, I don't see how your remarks apply to my sentence. I referred to the values being promoted to 0 and 1 during integral promotion that is being applied whilst evaluating `false < true`. Nothing wrong there. No one talking about value representations (yet). – Columbo Jan 05 '16 at 13:36
  • What I mean is that `bool(-1)` converts to `true` right away, there is no integral promotion to 1 involved. Any non-zero value converts to `true` right away. Thus, `bool(0) < bool(-1)` is absolutely identical to `false < true`, no 1 involved. I know, it's a nitpick... :-) I mostly mentioned because a lot of people assume `true == 1`, and that "convert to `1`" appearing there might suggest to the unwary reader that this is indeed the case. – Damon Jan 05 '16 at 13:41
  • 1
    @Damon But I haven't even remotely implied that that's the case. My sentence goes *"… is equivalent to false < true, which indeed holds (as [reason why false < true holds])"*. I am aware that there is no "promotion" involved in converting `-1` to `true`. I quoted the very paragraph that defines this conversion. – Columbo Jan 05 '16 at 13:50
  • @Damon I amended the wording, I hope the confusion is gone. – Columbo Jan 05 '16 at 13:54
  • @Damon According to 5\10.5 `true` is converted to `1` (4.5\6). – Eugene Zavidovsky Jan 05 '16 at 13:57
  • 1
    Hmm, the type traits probably need a blanket exemption from [res.on.functions]/2.5. – T.C. Jan 05 '16 at 15:51
  • 1
    Not my downvote (if you suspected that), I gave an upvote. – Damon Jan 05 '16 at 17:22
  • 1
    @EugeneZavidovsky: That is perfectly correct. But note the subtle wording: _converted to_. It is defined which integer values convert to `true` and it is also defined what single integer value `true` converts to. It is however not defined what it actually is (it could be any possible bit pattern). But it seems nobody understands where I'm coming from, so I'm probably not explaining well enough, or my thoughts are too contorted... :-) – Damon Jan 05 '16 at 17:28
10

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.

Cory Kramer
  • 114,268
  • 16
  • 167
  • 218
  • cppreference is not an authoritative source, merely interpretation. OP needed an authoritative information - the standard. – Ilya Popov Jan 05 '16 at 15:23
  • 2
    @IlyaPopov In most cases cppreference quotes the standard verbatim and notes which version (C++03/11/14/etc) it applies to. – Cory Kramer Jan 05 '16 at 15:31
  • Yeah, it's called cpp _reference_ for a reason. Unlike cplusplus.com. – edmz Jan 05 '16 at 18:53
10

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.

Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455