Why can't we do three-way comparison in C++?

Viewed 3400

To summarize it quickly, why isn't 2 < x < 9 equal to 2 < x && x < 9?

This is the test code I've written:

#include <iostream>

int main()
{
    int nums[] = { 5 , 1, 10};

    // We are gonna check if the number is in the range 2 - 9 
    for (auto e : nums)
    {
        if (2 < e < 9)
            std::cout << "2 < " << e << " < 9" << std::endl;

        if(2 < e && e < 9)
            std::cout << "2 < " << e << " and " << e << " < 9" << std::endl;
    }

    std::cin.get();
} 

Here is the output I'm getting:

2 < 5 < 9
2 < 5 and 5 < 9
2 < 1 < 9
2 < 10 < 9

It looks like only 2 < e && e < 9 works correctly.

6 Answers

The expression

2 < x < 9

is grouped as

(2 < x) < 9

And since 2 < x is either false (0) or true (1), and both are less than 9, it's always true.

So unless you use overloaded operators for a non-built-in type x (then a 3-way comparison would be possible if 2 < x were to return an instance of a proxy object on which < is defined), if you want to test if x is in the interval (2, 9) you need to write it the way you have.

Just because this language doesn't have that feature.

It could have been made to, but this would contrast with C in a non-compatible way.

C could have been made to, but the designers simply didn't do that.

You already have the correct way to do it.

Some different (and newer!) languages do support this.

the comparison operators in c++ takes as an argument two values. when you are writing a<b it is the same as operator<(a,b). and the return value of operator< is bool. when you are calling a function in c++, it is computing the expression of its arguments and then passing it to that function, so calling a<b<c is same as operator<(operator<(a,b),c)

basically, the answer to your question is that in c++ there is no comparison operator (less than, greater than...) that takes three arguments

If C++ chose to redefine a < b < c to better align with the mathematical notation, it would be ambiguous with the current meaning. The current meaning is a bit silly and is comparing bools with numbers, but there may be tricky code relying on this detail in production use.

And as C++ allows you to define your own types with operators, it would have to expose the new ternary < to you as well, so you could make one for your type. Which would open new cans of worms if you only define a binary < but no ternary < for your type - should the compiler start shouting at you?

C++ didn’t do it because that would have broken backward compatibility with C. So you would need to look another decade back for the answer.

I don’t know whether Brian Kernighan or Dennis Ritchie ever considered doing it the other way, or discussed their reasoning. I’m not aware of anyone requesting that specific feature. Their relational operators follow the same rules as other Algol-family languages.

One problem would have been that it makes the grammar ambiguous: 0 < x < 1 now has a very different meaning than (0 < x) < 1 or 0 < (x < 1). There are also the issue of how to parse a < b >= c or a < b == c. Remember, there was no Boolean type in K&R C. Logical operators returned int, since the result was presumed to be stored in a machine register.

Another possible reason behind it is that K&R C, according to its designers, is not a high-level language. Its basic operations generally correspond to machine instructions on the minicomputers it was developed on. So, a comparison was a machine instruction back then, and a double-comparison was not. It would’ve been strange, given the other choices they made, to introduce that particular syntactic sugar into the language just to make C code read a little more like a math paper.

Inside of if, there should be boolean condition. In 2 < x < 9, there are two conditions. And two conditions can't be calculated in C++ without operator between them. That's why we can't use 2 < x < 9.

Related