Exactly. The point is that the abstracted Point hides implementation details like the underlying representation, which is good as far as it goes because it prevents goofs like overflow and trucation due to incorrect type conversions, etc...
But the second you need to match that abstraction to the code from some other developer which doesn't use it, you need that underlying representation back again. And this kind of mismatch isn't an uncommon edge case, it's the pervasive truth of modern development.
Basically: C++ can't win here, and it would be better not to try IMHO.
> you need that underlying representation back again.
But that's kind of a problem: while the underlying representations might be perfectly compatible, and you might know that for sure, that is not at all guaranteed. If you have a function that takes (int, int, int, int) instead of (Point1, Point1), Point1 = (int, int), that would not help you very much when Point2 = (int32, int32) (e.g., sometimes libraries make assumptions about internal representations, sometimes they don't).
Using low-level primitives everywhere like that can be a very misleading kind of simplicity - it might disappear. That C++ makes you reckon with the possibility that Point1 /= Point2 is supposed to tell you something about what you can know for sure about those types.
I have this issue occasionally, switching between sf::Vector2, b2Vec2, gVec2, etc. Something like drawLine({v.x,v.y},{w.x,w.y}) usually works in a lot of these cases, but I don't mind explicitly converting between representations in order to fulfil an interface.
But the second you need to match that abstraction to the code from some other developer which doesn't use it, you need that underlying representation back again. And this kind of mismatch isn't an uncommon edge case, it's the pervasive truth of modern development.
Basically: C++ can't win here, and it would be better not to try IMHO.