Home > Enterprise >  Is it possible to pass a reference to a consteval function and use it as additional return value?
Is it possible to pass a reference to a consteval function and use it as additional return value?

Time:02-27

Sometimes the result of a function can not be represented by a single return value. For example: A function that intersects two lines. One might want the function to return both the actual point of intersection as well as their relation to each other (that is parallel, identical, intersecting or skewed).

Let us assume this example where the point of intersection is represented by some sort of class and the lines positional relation by an integer holding a specified value for each of the 4 possibilities:

int IntersectLines(Line l1, Line l2, Point& point);

Point point{};
int result = IntersectLines(l1, l2, point);

That is how I would have implemented it to this day but now I am wondering whether it is possible to have a similar implementation but with a consteval function. Line and Point have constexpr constructors and everything and the calculation itself can be compile time evaluated as well. The only problem is that I can't think of a way to have two return values. I already thought of std::pair but a solution more similar to passing a reference would be prefered. If such a solution doesn't exist I will have to fall back to std::pair.

It doesn't work by passing point by reference (Point& point) because "the expression did not evaluate to a constant" but passing by const reference (const Point& point) won't work either because I wouldn't be able to assign the result to point. Is there a way to get this working?

CodePudding user response:

You can return a std::pair<Point, Relationship>.

Example:

consteval std::pair<Point, Relationship> IntersectLines(const Line& l1, const Line& l2) 
{
    // replace with the real calc below, this is just for show:
    const Point pnt{l1.p1.x   l2.p1.x, l1.p1.y   l2.p1.y};
    const Relationship rel = Relationship::parallel;
    return {pnt, rel};
}

And call it like so:

int main() {
    constexpr Line l1({1,2}, {3,4}), l2({5,6}, {7,8});
    constexpr auto pr = IntersectLines(l1, l2);
    auto&[pnt, rel] = pr;

    return pnt.x   pnt.y; // 14
}

With optimization, the resulting assembly is likely to become something like

main:
        mov     eax, 14
        ret

Demo

CodePudding user response:

You cannot pass a reference to a consteval function and have the function modify the target of the reference, except if you do so in inside another consteval function.

The call to a consteval function must be on its own a constant expression, assuming it is not called inside another consteval function.

However, a constant expression cannot modify an object outside of the evaluation of the constant expression itself.


In both a consteval and usual function, you can however return a std::pair or std::tuple of multiple return values and e.g. retrieve them at the call site as a structured binding.

  • Related