Are C++ bools converted to ints during comparison? - c++

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

Related

Explain integer comparison with promotion

I'm trying to understand how integer promotion and comparison in and c++ application works.
#include <cstdint>
int main(void)
{
uint32_t foo = 20;
uint8_t a = 2;
uint8_t b = 1;
uint8_t c = 5;
if(foo == b*c) {}
if(foo == a) {}
if(foo == a + c) {}
if(foo == a + b*c) {}
return 0;
}
Only for the last comparison i get a compiler warning: "comparison between signed and unsigned integer expressions [-Wsign-compare]".
Why does this only happen in the last case but not in the others?
since the type of operands are different a set of implicit conversions take place to reach a common type.
For the binary operators (except shifts), if the promoted operands
have different types, additional set of implicit conversions is
applied, known as usual arithmetic conversions with the goal to
produce the common type (also accessible via the std::common_type type
trait)
because of integral types here integral conversions is applied to:
If either operand has scoped enumeration type, no conversion is performed: the other operand and the return type must have the same
type
Otherwise, if either operand is long double, the other operand is converted to long double
Otherwise, if either operand is double, the other operand is converted to double
Otherwise, if either operand is float, the other operand is converted to float
Otherwise, the operand has integer type (because bool, char, char8_t, char16_t, char32_t, wchar_t, and unscoped enumeration
were promoted at this point) and integral conversions are
applied to produce the common type, as follows:
If both operands are signed or both are unsigned, the operand with lesser conversion rank is converted to the operand with the
greater integer conversion rank
Otherwise, if the unsigned operand's conversion rank is greater or equal to the conversion rank of the signed
operand, the signed operand is converted to the unsigned
operand's type.
Otherwise, if the signed operand's type can represent all values of the unsigned operand, the unsigned operand is converted to the
signed operand's type Otherwise, both operands are converted to the
unsigned counterpart of the signed operand's type.
The same arithmetic conversions apply to comparison operators too.
from all this one can conclude since the rhs are all uint8_t the common type will be int, and then since the rhs is uint32_t the common type of == operator will be uint32_t.
but for some reason that I have no idea gcc don't do the last conversion while clang does it. see the gcc type conversion for + operator in godblot
It also could happen that the warning is a false warning and the conversion took place, as it happened for + operator.
See how clang sees the last if(cppinsights):
if(foo == static_cast<unsigned int>(static_cast<int>(a) + (static_cast<int>
(b) * static_cast<int>(c))))
Update:
I couldn't find a difference in the assembly generated by the two compilers and would agree with #M.M so, IMO it's a gcc bug.
It's a compiler "bug". To elaborate on this:
In general, comparison between signed and unsigned relies on implementation-defined quantities (the sizes/ranges of types). For example USHRT_MAX == -1 is true on common 16-bit systems, and false on common 32-bit systems. The answer by "oblivion" goes into more technical detail about this.
All of your code examples are well-defined and behave the same on all (conforming) systems.
The intent of this warning is twofold:
to alert you to code that might behave differently on other systems.
to alert you to code that might not behave as the coder intended.
However, in general. it's not such a simple job for the compiler's static analysis to sort out the first case, let alone the second case which is rather subjective.
IMO the warning, for your code, is a bug because the code is well-defined and there is nothing to warn about.
Personally I don't enable this warning: I'm familiar with the rules for signed-unsigned comparison and prefer to avoid mangling my code to suppress the warning.
Going to the opposite extreme, some people prefer to avoid all signed-unsigned comparisons in their code even when it is well-defined; and they would consider it a bug that the compiler doesn't warn about your first three code examples.
GCC has tended to err on the side of warning too much, but they are in the situation that they can't please everyone.

Is std::is_unsigned<bool>::value well defined?

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.

using Boolean to represent integers

In c++, bool is used to represent Boolean. that is it holds true or false. But in some case we can use bool to represent integers also.
what is the meaning of bool a=5; in c++?
"what is the meaning of bool a=5; in c++?"
It's actually equivalent to writing
bool a = (5 != 0);
"But in some case we can use bool to represent integers also."
Not really. The bool value only represents whether the integer used to initialize it was zero (-> false) or not (-> true).
The other way round (as mentioned in #Daniel Frey's comment) false will be converted back to an integer 0, and true will become 1.
So the original integer value's (or any other expression results like pointers, besides nullptr, or double values not exactly representing 0.0) will be lost.
Conclusion
As mentioned in LRiO's answer, it's not possible to store information other than false or true in a bool variable.
There are guaranteed rules of conversion though (citation from cppreference.com):
The safe bool problem
Until the introduction of explicit conversion functions in C++11, designing a class that should be usable in boolean contexts (e.g. if(obj) { ... }) presented a problem: given a user-defined conversion function, such as T::operator bool() const;, the implicit conversion sequence allowed one additional standard conversion sequence after that function call, which means the resultant bool could be converted to int, allowing such code as obj << 1; or int i = obj;.
One early solution for this can be seen in std::basic_ios, which defines operator! and operator void* (until C++11), so that the code such as if(std::cin) {...} compiles because void* is convertible to bool, but int n = std::cout; does not compile because void* is not convertible to int. This still allows nonsense code such as delete std::cout; to compile, and many pre-C++11 third party libraries were designed with a more elaborate solution, known as the Safe Bool idiom.
No, we can't.
It's just that there's an implicit conversion from integer to boolean.
Zero becomes false; anything else becomes true.
In fact this declaration
bool a=5;
is equivalent to
bool a=true;
except that in the first declaration 5 as it is not equal to zero is implicitly converted to true.
From the C++ Standard (4.12 Boolean conversions )
1 A prvalue of arithmetic, unscoped enumeration, pointer, or pointer
to member type can be converted to a prvalue of type bool. A zero
value, null pointer value, or null member pointer value is converted
to false; any other value is converted to true. For
direct-initialization (8.5), a prvalue of type std::nullptr_t can be
converted to a prvalue of type bool; the resulting value is false.
One more funny example
bool a = new int;
Of course it does not mean that we may use bool to represent pointers. Simply if the allocation is successfull then the returned poimter is not equal to zero and implicitly converted to true according to the quote I showed.
Take into account that till now some compilers have a bug and compile this code successfully
bool a = nullptr;
Though according to the same quote a valid declaration will look like
bool a( nullptr );
And, basically, I would opine that bool b=5 is "just plain wrong."
Both C and C++ have a comparatively weak system of typing. Nevertheless, I'm of the opinion that this should have produced a compile error ... and, if not, certainly a rejected code review, because it is (IMHO) more likely to be an unintentional mistake, than to be intention.
You should examine the code to determine whether b is or is not "really" being treated as a bool value. Barring some "magic bit-twiddling purpose" (which C/C++ programs sometimes are called-upon to do ...), this is probably a typo or a bug.
Vlad's response (below) shows what the standard says will happen if you do this, but I suggest that, "since it does not make human-sense to do this, it's probably an error and should be treated accordingly by the team.
A "(typecast)?" Maybe. A data-type mismatch such as this? Nyet.

The order of evaluation of equality operator in C++

Recently I have read some chapters C++ Primer, there is one things which made me confused.
The book says:
There are four operators that do guarantee the order in which operands are evaluated.
the logical AND (&&) operator,
the logical OR (||) operator,
the conditional (? :) operator,
and the comma (,) operator.
And then I saw these codes:
if (val == true) { /* ... */ } // true only if val is equal to 1!
Then the book explain:
If val is not a bool, then true is converted to the type of val before the == operator is applied.
That is, when val is not a bool, it is as if we had written
if (val == 1) { /* ... */ }
Here is my question:
Why is the bool type true converted to arithmetic type, not the val instead? Is it involve the order of evaluation of equality operator?
Just as the book says only four operators guarantee the order, not including equality operator. So the result of the expression above should be undefined, am I right? If not, what it should be?
Looking forward to your reply. Thanks in advance.
It has nothing to do with the order of evaluation. The comparison operators always perform the usual arithmetic conversions on their operands. These conversions apply equally to both operands (although the order is unspecified). In the case in which a bool is being compared to another integral type, the operand of type bool is always promoted to int first.
Relational operators such as ==, <=, etc., always produce boolean results. This is part of a set of rules known as "the usual arithmetic conversions".
Which side of the operator gets converted to what and in what order is somewhat esoteric and would concern compiler writers. It would be beyond the scope of a programmer.
In the condition of the if statement there are two different types
These are:
int
bool
In c++ types such as:
bool
char
short
are always converted to int first, hence in the condition of the if, true which is bool is converted to int and is 1 i.e, true is 1 and
val remains the same.

Are conditional expressions in C++ always of bool type?

In C conditional-oriented operators evaluate to either 1 or 0 of type int (even if it does have dedicated _Bool type). Referring to C11 N1570 draft:
C11 §6.5.8/6 Relational operators
Each of the operators < (less than), > (greater than), <= (less than
or equal to), and >= (greater than or equal to) shall yield 1 if the
specified relation is true and 0 if it is false.107) The result has
type int.
C11 §6.5.9/3 Equality operators
The == (equal to) and != (not equal to) operators are analogous to the
relational operators except for their lower precedence.108) Each of
the operators yields 1 if the specified relation is true and 0 if it
is false. The result has type int. For any pair of operands, exactly
one of the relations is true.
C11 6.5.13/3 Logical AND operator
The && operator shall yield 1 if both of its operands compare unequal
to 0; otherwise, it yields 0. The result has type int.
C11 6.5.14/3 Logical OR operator
The || operator shall yield 1 if either of its operands compare
unequal to 0; otherwise, it yields 0. The result has type int.
As I checked C++ seems to be different in this matter, as in following example (see http://ideone.com/u3NxfW):
#include <iostream>
#include <typeinfo>
int main() {
double x = 10.0;
std::cout << typeid(x <= 10.0).name() << std::endl;
return 0;
}
outputs b, which as I guess indicates bool type. Does C++ guarantee that all of these operators always evaluate to bool type (in contrast to C)?
No, because of operator overloading. This has been mentioned before, but I can give the real life example of expression templates. The idea, generally, is to allow writing "lazy" expressions (that is, really, function objects or ASTs) with syntax that is very similar to the normal, eager use of logical operators. Typically, many other operators, in particular arithmetic operators are also, overloaded.
For instance, one design goal of Boost.Lambda was to simplify the use of algorithms:
std::string str;
// ...
std:.string::iterator firstA = std::find_if(str.begin(), str.end(), _1 == 'a' || _1 == 'A');
Previously, in "pure" C++98, it was generally necessary to write numerous named functions or function objects before many standard algorithms could be used effectively.
Since C++11, Boost.Lambda is not as useful any more since lambda expressions have been added to the core language. There are still numerous EDSLs (embedded domain-specific languages) where C++11 lambdas cannot replace expression templates though, e.g. you may want to generate SQL command strings directly from a C++ EDSL in a way similar to LINQ in .NET, but as a portable library solution. Another example: the VexCL library uses expression templates to generate GPU kernels.
This is probably the only legitimate use of non-bool return types for overloaded logical operators, but it's not generally considered esoteric.
Contrary to C, in C++, relational operators, equality operators and logical operators (logical AND, logical OR and logical negation) all yield a value of type bool.
For example:
(C++11, 5.9p1 Relational operators) "[...] The type of the result is bool."
EDIT: for the sake of completeness, all the operators listed above can be overloaded and thus the resulting type can be changed. See Arne Vogel answer for real life example.