Ardent Coder
Ardent Coder

Reputation: 3985

How to return one out of multiple values from a single C++ return statement?

return a or b or c or d;

That statement returns either true or false in C++, and I know the reason for this.

But I need a workaround so that I can return the first non-zero value via that return statement (or something similar) like it happens in Python.

I am not looking for conditional statements as it looks untidy sometimes.

Basically, can the following code be shortened via a macro or something else?

int fun(int a, int b, int c, int d)
{
    return a ? a : b ? b : c ? c : d;
}

Upvotes: 1

Views: 253

Answers (2)

Ardent Coder
Ardent Coder

Reputation: 3985

The task can be accomplished by using the necessary function in the return statement.

For example, instead of a macro, I used a templated function which accepts the parameters in std::initializer_list:

template <typename T>
T OR(const std::initializer_list<T>& args)
{
    for (const T& arg : args)
        if (arg)
            return arg;
    return T{};
}

It can be used as follows for the given problem:

return OR({a, b, c, d});

The problem in that link can also be solved in this way:

return OR({(m + s - 1) % n, n});

Note that it depends on the implicit boolean conversion of a given type T. So, for example, an empty std::string is not false. Also, a user-defined type should have operator bool() const in order to comply with this workaround.

P.S. While I was trying to ask my mistake in a variadic template solution in the question, I discovered this solution myself :P

Note:

See this answer to know the limitations of this way when working with more complex designs.

Upvotes: 4

463035818_is_not_an_ai
463035818_is_not_an_ai

Reputation: 122133

I would write the function like this:

int fun(int a, int b, int c, int d) {
    if (a) return a;
    else if (b) return b;
    else if (c) return c;
    return d;
}

It is clean and short. I could stop here, but lets explore what can be done...

There exists an algorithm that already almost does what you want. A slight modification of the solution in this answer:

#include <algorithm>
#include <initializer_list>

template <typename T>
T first_non_zero_or_zero(const std::initializer_list<T>& args)
{
    auto it = std::find_if_not(args.begin(),args.end(),[](auto v){ return v==0;});    
    return (it != args.end()) ? *it : 0;
}

The drawback of using a function for boolean expressions is no short-cuirciting. If you call the function via:

auto x = first_non_zero_or_zero( { foo(), expensive_function() });

Then expensive_function must be called, no matter what foo returns. The way to restore the ability to short-circuit is to pass callables instead, that would be

template <typename F>
auto first_non_zero_or_zero(F f){ return f();}

template <typename First,typename...F>
auto first_non_zero_or_zero(First f,F... others){    
    if (auto temp = f()) return temp;
    return first_non_zero_or_zero(others...);
}

int foo(){ return 0;}
int expensive_function(){ return 42;}

int main()
{
    std::cout << first_non_zero_or_zero(foo,expensive_function);
    return 0;
}

However, this will make calls unnecessarily verbose when called with simple ints, as you need to wrap them in a callable:

int fun(int a,int b,int c) {
    first_non_zero( [](){ return a;},
                    [](){ return b;},
                    [](){ return c;})
}

Conclusion: Don't make things more complicated than necessary. Functions should do one thing. The one thing your fun does is to return the first non-zero of 4 integers and a if-else is the most simple way to get that done.

Upvotes: 4

Related Questions