Should the boolean value be truncated to either true or false when assigned?

I found a difference in the value stored in the bool variable (btw Visual-C ++ and clang ++) in the case where the stored value is neither true nor false (if it has been corrupted somehow) and I'm not sure that this is a Visual C ++ bug or if it's just UB I should ignore.

Let's take the following example:

#include <cstdint>
#include <iostream>
#include <string>
#include <limits>
bool inLimits(bool const v)
{
    return (static_cast<std::int32_t>(v) >= static_cast<std::int32_t>(std::numeric_limits<bool>::min()) && static_cast<std::int32_t>(v) <= static_cast<std::int32_t>(std::numeric_limits<bool>::max()));
}
int main()
{
    bool b{ false };
    bool const* const pb = reinterpret_cast<bool const*>(&b);
    std::uint8_t * const pi = reinterpret_cast<std::uint8_t*>(&b);

    std::cout << "b: " << b << " pb: " << (*pb) << " pi: " << std::to_string(*pi) << std::endl;
    std::cout << "b is " << (inLimits(b) ? "" : "not ") << "in numeric limits for a bool" << std::endl;

    *pi = 3; // Simulate a bad cast during boolean creation
    bool const b2{ b };
    bool const b3{ *pb };

    std::cout << "b: " << b << " pb: " << (*pb) << " pi: " << std::to_string(*pi) << std::endl;
    std::cout << "b2: " << b2 << " b3: " << b3 << std::endl;

    std::cout << "b is " << (inLimits(b) ? "" : "not ") << "in numeric limits for a bool" << std::endl;
    std::cout << "b2 is " << (inLimits(b2) ? "" : "not ") << "in numeric limits for a bool" << std::endl;
    std::cout << "b3 is " << (inLimits(b3) ? "" : "not ") << "in numeric limits for a bool" << std::endl;

    return 0;
}

      

This is the output of Visual-C ++

b: 0 pb: 0 pi: 0
b is in numeric limits for a bool
b: 3 pb: 3 pi: 3
b2: 3 b3: 3
b is not in numeric limits for a bool
b2 is not in numeric limits for a bool
b3 is not in numeric limits for a bool

      

and this is the output from clang ++

b: 0 pb: 0 pi: 0
b is in numeric limits for a bool
b: 1 pb: 1 pi: 3
b2: 1 b3: 1
b is in numeric limits for a bool
b2 is in numeric limits for a bool
b3 is in numeric limits for a bool

      

It looks like there is a constraint check in clang ++ when constructing a new boolean value by value as well as when using it with a stream operator.

Should I just ignore this, or is this a bug that only Visual-C ++ has? Thank!

Edit: For those who didn't understand the purpose of the sample, it was just a showcase to "simulate" memory corruption or an error in another piece of code that caused the boolean to be initialized with something other than true or false, regardless of whether binary bool view.

(I was wondering if I need to protect my code from being misused elsewhere by using for example assert, but only if that behavior is not UB)

Second edit: Added numeric_limits code.

+3


source to share


4 answers


The standard does not specify what a value representation is bool

. Compilers are free to create their own specifications.

Your data suggests that VC ++ requires to true

be rendered as soon as the LSB set, whereas clang ++ allows any non-null representation true

.



For VC ++, your code causes undefined behavior on a string bool const b2{ b };

, especially when it tries to read a value from b

. The bits set in the store for b

do not match the value b

and the standard does not define what happens in this situation, so this behavior is undefined.

When undefined behavior occurs, there are no guarantees; all program output is meaningless. You cannot infer anything based on the output operators that appear after this point (or even before that point, in fact).

+2


source


"in case the stored value is neither true nor false"

Why do you think the case? C ++ doesn't limit binary representation bool

. In some compilers it true

may be represented 00000011

, while other compilers may choose false

how 00000011

.



But really, neither GCC nor MSVC uses this bit pattern for the value bool

. This does indeed make the Behavior Undefined. UB can never be a compiler error. The error is that the implementation doesn't work as expected, but UB specifically means that any actual behavior is acceptable.

+3


source


Since I really didn't find any information about pointer-to-bool (or equivalent) casts in the C ++ standard (if their use is defined), I didn't want to post this as an answer. But with a second thought, I can also post it - it can be figured out by other people.

First of all, the C ++ 14 standard defines bool

as:

[basic.fundamental]

  1. Bool values ​​are either true or false. [Note. There are no signed, unsigned, short or long types or bool values. - end note] Values ​​of bool type participate in integral promotions (4.5)

Since he participates in integral promotions, the following promotion is determined for him:

[conv.prom]

  1. A bool prvalue can be converted to an int prvalue, whereby false becomes zero and true becomes one.

And, since you are typing with std::ostream::operator<<

, for bool

, it is defined like this:

[ostream.inserters.arithmetic]

  • The num_get <> and num_put <> classes handle language-specific numeric formatting and parsing.

Since it uses num_put<>

for actual output, its output related snippet bool

is defined as:

[facet.num.put.virtuals]

  1. If (str.flags () and ios_base :: boolalpha) == 0 returns do_put (out, str, fill, (int) val)

Since you are not using boolalpha

in the example you are showing, the typical Integral Advertising Rules (described above) should apply.

Also, I still can't explain why std::to_string(*pi)

after it *pi = 3

still prints 3

in both cases, but could be related to:

[expr.reinterpret.cast]

  1. [Note. The mapping performed by reinterpret_cast may or may not produce a different representation from the original value. - end note]
+1


source


Not sure if it helps, but g ++ exhibits the same behavior as Visual-C ++.

This is the output I got:

b: 0 pb: 0 pi: 0
b: 3 pb: 3 pi: 3
b2: 3 b3: 3

From what I understand (I am an expert on C ++ compilers) reinterpret_cast

instructs the compiler to treat the collection of bits as a new type. So when you tell the compiler to reinterpret the gate address as an 8-bit integer, it essentially also converts the original boolean to an 8-bit integer (if that makes sense).

So, if my interpretation is correct (it is not), it may be a "bug" in clang ++ and not Visual or g ++. reinterpret_cast

not well supported across compilers, so this behavior is definitely worth noting when deciding what to use if necessary for any reason.

change

I just realized that this doesn't explain why b2 and b3 are 3 (non-boolean). I wouldn't think it would be wise to treat the new booleans as 8-bit integers regardless reinterpret_cast

, so take this for what it's worth from the 1 rep guy :)

0


source







All Articles