Call an inline friend from a method instead of itself

Viewed 112
struct P {
  int x, y;

  friend P operator-(P u, P v) { return {u.x - v.x, u.y - v.y}; }

  friend int cross(P u, P v) { return u.x * v.y - u.y * v.x; }
  int cross(P u, P v) const { return cross(u - *this, v - *this); }
};

The method is an infinite loop (it will call itself instead of the friend).

Is there any way to get around this issue without changing the interface (the names)?

4 Answers

It is possible with a declaration inside the member function:

#include <stdio.h>

struct Foo {
  friend int add(int a, int b) {
    return a + b;
  }

  int add(int a, int b) const {
    int add(int, int); // function declaration
    return add(a, b);  // call that declared function
  }
};

int main() {
  Foo foo;
  printf("%d", foo.add(1, 2)); // prints 3
}

Demo

You can't have two member functions with the same name and the same signature in a struct (or class).

One solution is to move the friend function out of the struct, so that it can be referred to with a global namespace specifier ::

#include <iostream>

int cross(int i) { return 42; }

struct P {
    friend int cross(int i);
    int cross(int i) const { return ::cross(i - 1); }
};

int main() {
    P p = {};
    std::cout << p.cross(7) << std::endl;
}

The original code produced two warnings, very descriptive:

1 > 1.cpp(6, 25) : warning C4514 : 'cross' : unreferenced inline function has been removed

1 > 1.cpp(7) : warning C4717 : 'P::cross' : recursive on all control paths, function will cause runtime stack overflow

Re: without changing the interface (the names) - you could simply rename your friend function and even make it private; it is not a part of the interface.

Ayxan Haqverdili's answer seems to be cleanest possible, and abides by the ISO. However, pre-gcc 11.0 and pre-clang 8.0 appear to have bugs that cause this to fail.

Luckily in this case there's an alternative that uses argument-dependent lookup:

#include <iostream>

class Foo
{
    friend int Bar (Foo)
    {
        return 13;
    }
    friend int RedirectToBar (Foo);
    
public:

    int Bar (Foo)
    {
        return RedirectToBar(Foo());
    }
};

int RedirectToBar (Foo)
{
    return Bar(Foo());
}

int main ()
{
    std::cout << Foo().Bar(Foo()) << std::endl;
}

Live demo

Keep in mind that this only works because some of the arguments to the function are an associated class.

If that's not the case and you really want to keep this design and really need compatibility with older compilers, then we can add in a dummy associated class without breaking the interface by adding it as a default parameter:

#include <iostream>

int RedirectToBar ();

class Foo
{
    struct Key {};

    friend int Bar (Key = Key())
    {
        return 13;
    }
    friend int RedirectToBar ();
    
public:

    int Bar ()
    {
        return RedirectToBar();
    }
};

int RedirectToBar ()
{
    return Bar(Foo::Key());
}

int main ()
{
    std::cout << Foo().Bar() << std::endl;
}

Live demo

However, it's important to add that all of this is a big code smell. Although I don't know the real-world problem of the OP, this seems like a terrible hack to get around bad design choices.

I ended up using this solution similar to Peter's and Yksisarvinen's:

struct P {
    int x, y;

    friend P operator-(P u, P v) { return P{u.x - v.x, u.y - v.y}; }

    friend int cross(P u, P v);
    int cross(P u, P v) const;
};

int cross(P u, P v) { return u.x * v.y - u.y * v.x; }
int P::cross(P u, P v) const { return ::cross(u - *this, v - *this); }

Not quite sure yet why the compiler doesn't find ::cross if I write it inside of P.

Note, there is one potential drawback: we have to specify the return type of both functions (can't have it deduced with auto), but this was not needed in this case.

Related