Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Test if all elements are equal with C++17 fold-expression

I have a function taking a variadic parameter pack and at the beginning I want to check that all elements compare equal. Can I somehow use the new C++17 fold-expressions to write that succinctly as a one-liner? I was thinking

template<typename... Args>
void func (Args... args)
{
    ASSERT ((args == ...));

    // more code here...
}

but this doesn't work, as it compiles to code that first properly compares the last two arguments, but then compares the third-last argument to the result of the first comparison, which is a bool. What use-case could this type of fold expression possibly have (similar for args < ...)? Is there any chance I can avoid writing a dedicated recursive template to do this?

like image 721
Toby Brull Avatar asked Oct 18 '17 08:10

Toby Brull


1 Answers

The reason that doesn't work, unfortunately, is due to the fact that boolean operators don't chain in C++ like they do in other languages. So the expression:

a == (b == c)

(what your fold-expression would expand to) would compare a to either true or false, nothing to do with what b or c actually are. I was hoping the operator<=> would add chaining but apparently that part was dropped.

The fixes are that you have to break up the comparisons:

(a == b) && (b == c)

Of course that doesn't lend itself to folding very well, but you could instead compare everything to the first element:

(a == b) && (a == c)

Which is to say:

((a0 == args) && ... )

At that point, we just need to be able to pull out the first element. No problem, that's obviously what lambdas are for:

template <class... Args>
constexpr bool all_equal(Args const&... args) {
    if constexpr (sizeof...(Args) == 0) {
        return true;
    } else {
        return [](auto const& a0, auto const&... rest){
            return ((a0 == rest) && ...);
        }(args...);
    }
}
like image 196
Barry Avatar answered Nov 19 '22 02:11

Barry