154

Does the C++ specification define:

  1. the existence of the 'less than' operator for boolean parameters, and if so,
  2. the result of the 4 parameter permutations?

In other words, are the results from the following operations defined by the specification?

false < false
false < true
true < false
true < true

On my setup (Centos 7, gcc 4.8.2) , the code below spits out what I'd expect (given C's history of representing false as 0 and true as 1):

false < false = false
false < true = true
true < false = false
true < true = false

Whilst I'm pretty sure most (all?) compilers will give the same output, is this legislated by the C++ specification? Or is an obfuscating, but specification-compliant compiler allowed to decide that true is less than false?

#include <iostream>

const char * s(bool a)
{
  return (a ? "true" : "false");
}

void test(bool a, bool b)
{
  std::cout << s(a) << " < " << s(b) << " = " << s(a < b) << std::endl;
}

int main(int argc, char* argv[])
{
  test(false, false);
  test(false, true);
  test(true, false);
  test(true, true);
  return 0;
}
Deduplicator
  • 41,806
  • 6
  • 61
  • 104
duncan
  • 2,233
  • 2
  • 14
  • 21
  • g++-4.9.0 and clang++-3.5.0 on Linux think it's unremarkable: http://coliru.stacked-crooked.com/a/2174ebc466d0c445 – Deduplicator Oct 01 '14 at 14:44
  • 6
    @Ulterior There are valid uses. Such as using `std::min` on `std::vector` as `&&`. – Angew is no longer proud of SO Oct 01 '14 at 14:45
  • 19
    @Ulterior if you can figure out a good question that hasn't yet been asked after all these years of StackOverflow, you *deserve* some points. It's not trolling. – Mark Ransom Oct 01 '14 at 16:06
  • 35
    @Ulterior The motivation for asking is genuine: I'm fairly new to C++ (coming from C) and want to store some objects in a std::set<>. My implementation of my object's < operator is primarily based on a boolean property of the object, followed by other secondary identifying properties. When iterating over the set, I want to be sure the 'false' objects come first. While it works for me here and now, I'm looking for reassurance it's guaranteed to work across platforms (including embedded ones) without having to resort unnecessarily to the use of (a ? 1 : 0), or similar, in my object's < operator. – duncan Oct 01 '14 at 16:36
  • 26
    A disturbing consequence is that `p <= q` means `p implies q` when `p` and `q` are of type bool! – Theodore Norvell Oct 02 '14 at 16:57
  • 2
    @TheodoreNorvell, I'm not clear on what you find disturbing about that. Could you say a bit more? – Technophile Oct 02 '14 at 17:36
  • 4
    @Technophile Presumably what's disturbing is that `<=` might be inadvertently read as a leftarrow, and that the "only if" (i.e., "[materially] implies") rightarrow is sometimes typeset or informally written similarly to `=>` (i.e., with a doubled shaft resembling `=`). A leftarrow is even sometimes read as "if," though I believe this is far less common than the use of a rightarrow for "only if." – Eliah Kagan Oct 02 '14 at 19:16
  • 1
    @Technophile Exactly as Eliah said. `p <= q` looks like `q` implies `p`, but in fact means `p` implies `q`. – Theodore Norvell Oct 03 '14 at 00:16
  • 2
    Apparently it is well defined. That is sad. Like a language striving too hard to be popular. While it might be a convenience to "order" true and false, it is logically absurd. So if ordering true and false is a design consideration in a model, IMO, the author should be forced to choose the ordering explicitly. Heck, the next step might be false or true is less than true or true - because two true beats one true. – Fred Mitchell Oct 08 '14 at 02:32
  • 1
    _"is this legislated by the C++..."_ I sure hope programs don't start writing laws... – Scott Solmer Oct 08 '14 at 11:58
  • @Okuma.Scott many compilers have extensions that are not part of the standard. Also there are areas where the standard declares that the behavior is implementation defined(*different from undefined behavior*) which means it could vary from compiler to compiler and still be allowed by the standard. So this is an important distinction and this type of question usually makes it a *language lawyer* question. – Shafik Yaghmour Oct 08 '14 at 12:42
  • Be very careful with this if you interact with COM and/or Sql Server as they sometimes define true as -1. – devdimi Oct 06 '14 at 15:00

4 Answers4

207

TL;DR:

The operations are well defined according to the draft C++ standard.

Details

We can see that by going to the draft C++ standard section 5.9 Relational operators which says (emphasis mine going forward):

The operands shall have arithmetic, enumeration, or pointer type, or type std::nullptr_t. The operators < (less than), > (greater than), <= (less than or equal to), and >= (greater than or equal to) all yield false or true. The type of the result is bool

and bools are arithematic types from 3.9.1 Fundamental types

Types bool, char, char16_t, char32_t, wchar_t, and the signed and unsigned integer types are collectively called integral types.

and

Integral and floating types are collectively called arithmetic types.

and true and false are boolean literals from 2.14.6 Boolean literals:

boolean-literal:
    false
    true

Going back to section 5.9 to see the mechanics of the relational operators further, it says:

The usual arithmetic conversions are performed on operands of arithmetic or enumeration type.

the usual arithmetic conversions are covered in section 5 which says:

Otherwise, the integral promotions (4.5) shall be performed on both operands

and section 4.5 says:

A prvalue of type bool can be converted to a prvalue of type int, with false becoming zero and true becoming one.

and so the expressions:

false < false
false < true
true < false
true < true

using these rules become:

0 < 0
0 < 1
1 < 0
1 < 1
Shafik Yaghmour
  • 143,425
  • 33
  • 399
  • 682
  • 6
    Nice, that's about as explicit as any answer could possibly be, while still easy to read. A nit: I think you bolded the wrong "type": "The **operands shall have arithmetic**, enumeration, or pointer **type**, or type std::nullptr_t." Adding parentheses for clarity gives ((arithmetic, enumeration, or pointer) type) or (type std::nullptr_t). –  Oct 02 '14 at 09:18
  • Not that it changes your answer, but N3485 [over.built]/12: *For every pair of promoted arithmetic types L and R, there exist candidate operator functions of the form* ... *bool operator – chris Mar 18 '15 at 03:26
  • @chris I am not super familiar with that section so I would have to think about it but I don't think the answer changes from what I can see. – Shafik Yaghmour Mar 18 '15 at 03:40
  • Yeah, the promotion is the first thing to happen either way. – chris Mar 18 '15 at 03:56
63

Boolean values are subject to the usual integer promotions, with false defined as 0 and true defined as 1. That makes all the comparisons well defined.

Mark Ransom
  • 271,357
  • 39
  • 345
  • 578
  • 2
    ...and relational operators are specified to perform the usual arithmetic conversions (which includes integer promotions) on operands of arithmetic or enumeration type. – T.C. Oct 01 '14 at 14:44
  • 5
    I like that this answer is shorter than Shafik's, but I think the key point that `false` is defined as `0` and `true` is defined as `1` *in the standard* (rather than just by common practice) needs evidence to back it up. – KRyan Oct 01 '14 at 16:36
  • @KRyan what, you're not going to take my word for it? :) Before there was a `bool` type, before there was even C++, the result of a boolean operation was defined as `0` for false and `1` for true. I wouldn't be surprised if you can find it in K+R. – Mark Ransom Oct 01 '14 at 16:42
  • @MarkRansom huh, for some reason I thought C had `bool` from the start (though I realize `0` and `1` were used before `bool` existed in earlier languages). But then I've only so much as *seen* K+R code once in my life, so my knowledge of that history is suspect. – KRyan Oct 01 '14 at 16:46
  • 1
    @KRyan I can't go back quite as far as K+R, but I dug out my copy of the 1990 ANSI C Standard. Section 6.3.8 says "Each of the operators `` (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. The result has type `int`." – Mark Ransom Oct 02 '14 at 00:30
  • 1
    The biggest problem IIRC was that in K&R, `enum bool { false = 0, true = 1}` was legal but didn't define an `operator – MSalters Oct 02 '14 at 08:19
22

According to the C++ Standard (5.9 Relational operators)

2 The usual arithmetic conversions are performed on operands of arithmetic or enumeration type.

and

1...The type of the result is bool.

and (3.9.1 Fundamental types)

6 Values of type bool are either true or false.49 [ 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).

and (4.5 Integral promotions)

6 A prvalue of type bool can be converted to a prvalue of type int, with false becoming zero and true becoming one.

So in all your examples true is converted to int 1 and false is converted to int 0

These expressions

false < false
false < true
true < false
true < true

are entirely equivalent to

0 < 0
0 < 1
1 < 0
1 < 1
doppelgreener
  • 5,752
  • 8
  • 42
  • 59
Vlad from Moscow
  • 224,104
  • 15
  • 141
  • 268
8

Boolean false is equivalent to int 0, and boolean true is equivalent to int 1. So this explains why the expression false < true => 0 < 1 is the only one which returns true.

Ziezi
  • 6,049
  • 3
  • 34
  • 45