Why comparing three variables together with == evaluates to false?

Viewed 477

The output of the following program is "they are not equal", but I'd expect "they are equal" as the three compared variables (x,y, and z) are equal. Why?

#include <iostream>

int main()
{
    int y, x, z;
    y = 3;
    x = 3;
    z = 3;

    if (x == y == z)
    {
        std::cout << "they are equal\n";
    }
    else
    {
        std::cout << "they are not equal\n";
    }
}
2 Answers

This is because of the way expression and types are evaluated.

Let's evaluate the leftmost ==

x == y ...

This evaluates to true. Let's rewrite the expression:

//  x == y
if (true   == z) {
    // ...
}

The true is a boolean value. A boolean value cannot be directly compared to an int. A conversion from the boolean to an integer must occur, and the result is 1 (yes, true == 1). Let's rewrite the expression to its equivalent value:

//  true
if (1    == z) {
    //    ^--- that's false
}

But z isn't equal to 1. That expression is false!

Instead, you should separate both boolean expressions:

if (x == y && y == z) {
    // ...
}

And now some practical C++17 applications. No SFINAE necessary.

//----------------------------------
// constexpr lambda requires C++17
auto eq3 = [] (auto v1, auto v2, auto v3) constexpr -> bool
{
    return ( v1 == v2 ) && ( v2 == v3 );
};

Usage is simple but fully compile time

constexpr auto same_ = eq3(42,42,42);
std::bool_constant< eq3(42,42,42) > twins_ ;

For comparing a whole sequences of values, concept is the same, impementation is a little bit more involved.

template<typename ... T>
constexpr bool all_equal ( const T & ... args_ )  
{
    if ((sizeof...( args_) ) < 2) return true;
    // for non recursive version
    const auto il_ = { args_ ... };
    // compare them all to the first
    auto first_ = *(il_.begin()) ;
    // assumption
    bool rezult_{ true }; 
    for ( auto && elem_ : il_) {
        // yes I know, first cycle compares first_ to itself ...
        rezult_ = rezult_ && ( first_ == elem_ );
        // short circuit-ing
        if (!rezult_) break;
    }
    return rezult_; 
};

"just" a function, compile time, again no variadic template trickery.

    bool_constant< all_equal(42,42,42,42,42,42,42) > same_ ;
    cout << endl << boolalpha <<  same_() ;

    bool_constant< all_equal(42,43,44,45,46,47) > not_same_ ;
    cout << endl << boolalpha <<  not_same_() ;

Mandatory Wandbox is here.

ps: somewhat predictable all_equal does not compile using the very latest CL also known as MSVC or Visual Studio.

Related