C++20 return a tuple in ternary statement

123 views Asked by At

I have a small function that returns three values. I use a std::tuple to facilitate returning of multiple values.

What the function returns is determined by one variable, if the variable is not zero it returns one tuple, else it returns another one. Because I use C++20 I just use curly brackets ({}) to build the tuples.

I think using a ternary statement would be perfect, but the following doesn't work:

#include <tuple>

using std::tuple;

double hue(double b, double g, double r, double d, double i);

tuple<double, double, double> HSL_pixel(double b, double g, double r, double i, double x, double z) {
    double s = x + z;
    double d = z - x;
    double avg = s / 2;
    return d ? {hue(b, g, r, d, i), d / (1 - abs(s - 1)), avg} : {0.0, 0.0, avg};
}

In Visual Studio 2022, an error is detected at the first opening curly bracket position, it says: "expected an expression".

Changing to if else statement fixed the problem:

tuple<double, double, double> HSL_pixel(double b, double g, double r, double i, double x, double z) {
    double s = x + z;
    double d = z - x;
    double avg = s / 2;
    if (d) {
        return { hue(b, g, r, d, i), d / (1 - abs(s - 1)), avg };
    } 
    else {
        return { 0.0, 0.0, avg };
    }
}

But I want to use ternary. How can I use ternary in this case?

2

There are 2 answers

0
Barry On BEST ANSWER

Grammatically, you cannot use a braced-init-list with the conditional operator. So just this is ill-formed:

int a = true ? 1 : {};

You have to write this instead:

int b = true ? 1 : int{};

Which in your example that's:

cond ? tuple{a, b, c} : tuple{d, e, f}

instead of:

cond ? {a, b, c} : {d, e, f}

Or, if you really prefer to use braced-init-list, then you'll have to use if/else (or just if). Just a style choice at that point.

0
Jan Schultke On

As @Barry has already pointed out, a braced-init-list cannot appear as one of two expressions in a conditional operator.

Besides that issue, returning tuples is also not a good idea because it's unclear from the signature of a function what tuple<double, double, double> actually means. You could improve code quality a lot by using simple structs instead:

struct rgb_color {
    double r, g, b; // note: float precision is almost always sufficient for colors,
                    //       your monitor can only handle 8 or 10 bits anyway
    // define all comparisons
    friend auto operator<=>(const rgb_color&, const rgb_color&) = default;
};

struct hsl_color {
    double h, s, l; // consider using hue, saturation, lightness instead

   friend auto operator<=>(const hsl_color&, const hsl_color&) = default;
};

With simple structs like these, you get almost all the functionality of std::tuple that's worth caring about, and code becomes much clearer:

double hue(rgb_color color, double d, double i);

hsl_color HSL_pixel(rgb_color color, double i, double x, double z) {
    double s = x + z;
    double d = z - x;
    double avg = s / 2;
    return d ? hsl_color{hue(color, d, i), d / (1 - abs(s - 1)), avg}
             : hsl_color{0.0, 0.0, avg};
}

I'm still not sure what d, i, x and z mean, but they're probably worth bundling up too in some way.