2

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?

hanslhansl
  • 63
  • 4
  • Please clarify your specific problem or provide additional details to highlight exactly what you need. As it's currently written, it's hard to tell exactly what you're asking. – Community Feb 26 '22 at 12:21

2 Answers2

3

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

Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
  • 1
    I have never seen this structured bindings stuff before. It's pretty straight forward and will do just fine in my case. – hanslhansl Feb 26 '22 at 16:16
2

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.

user17732522
  • 53,019
  • 2
  • 56
  • 105