0

I have a struct that looks like this:

struct Lip {
    int     x;

    friend Lip mkLip(Tester t, bool full = false);

    bool operator == (Lip p) const { return x == p.x; }
    bool operator != (Lip p) const { return x != p.x; }
    bool operator <  (Lip p) const { return x < p.x;  }
};

The problem with this is that it throws error on Mac regarding the friend declaration specifying a default argument. I read that one way is to have a non-friend declaration somewhere out, but as you can see my friend function has the same return type as the actual struct itself. What's the proper way to fix this here?

terett
  • 355
  • 1
  • 8
  • You can have a member function that returns `Lip`. If you want to make it a friend with a default argument, you'll need to define it inside the class. – cigien Oct 02 '20 at 22:05

2 Answers2

1

All your members are public, so you don't need a friend at all.

Just have a nice, normal function declaration:

struct Lip
{
    int x;

    bool operator==(Lip p) const { return x == p.x; }
    bool operator!=(Lip p) const { return x != p.x; }
    bool operator<(Lip p) const { return x < p.x; }
};

Lip mkLip(Tester t, bool full = false);

If you then introduce private members that the function needs access to, make it a factory in the form of a static member function:

struct Lip
{
  private:
    int x;

  public:
    static Lip mkLip(Tester t, bool full = false);
    
    bool operator==(Lip p) const { return x == p.x; }
    bool operator!=(Lip p) const { return x != p.x; }
    bool operator<(Lip p) const { return x < p.x; }
};

static member functions have access to private members of an instance (e.g. one that they create!).


You should also consider making the class's constructor private if it can't be safely constructed except when using mkLip to do so.


Note that introducing non-static private things to your class will lose it its aggregate status; you may or may not care.

Asteroids With Wings
  • 17,071
  • 2
  • 21
  • 35
1

You can indeed give it a definition inside the class:

friend Lip mkLip(Tester t, bool full = false) { return {}; }

However, this will affect name lookup on mkLip. Chances are you want mkLip to use normal lookup rules and lie outside of the class. In this case, you can always forward-declare the return type:

// Before class
struct Lip;
Lip mkLip(Tester t, bool full = false);

...

// Inside class
friend Lip mkLip(Tester t, bool full);

...

// After class
Lip mkLip(Tester t, bool full) { return {}; }
chris
  • 60,560
  • 13
  • 143
  • 205
  • 1
    People (myself included) do tend to forget that return types can be incomplete, even though we write `void foo();` all the time ;) – Asteroids With Wings Oct 02 '20 at 22:18
  • I see the OP has upvoted and accepted already (within 30s!). Having a `friend` here is still completely pointless & unidiomatic, and makes the code way more complex to read than necessary. Though this is the right solution for someone locked into that decision. – Asteroids With Wings Oct 02 '20 at 22:20