3

Consider:

struct Y {
  Y(float f) : f(f) {}
  float f;
};

struct X {
  X(Y y) : i(y.f) {}
  int i;
  friend bool operator==(X x1, X x2) {
    return x1.i == x2.i;
  }
};

int main()
{
    return Y(1) == Y(2); // ERROR
}

This causes the following error on MSVC and a similar one on Clang:

'==': candidate function(s) not accessible
could be the friend function at '..\main.cpp(11)' : '=='  [may be found via argument-dependent lookup]

If I move the definition of the friend function out of the class:

struct X {
    X(Y y) : i(y.f) {}
    int i;
    friend bool operator==(X x1, X x2);
};

inline bool operator==(X x1, X x2)
{
    return x1.i == x2.i;
}

The expression in main() above compiles fine.

Is this mandated by the standard or a bug? If it's mandated: why?

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
Marc Mutz - mmutz
  • 24,485
  • 12
  • 80
  • 90

2 Answers2

5

This is a C++ oddity.

[C++14: 11.3/7]: [..] A friend function defined in a class is in the (lexical) scope of the class in which it is defined. A friend function defined outside the class is not (3.4.1).

This means that the operator call would work if you made it from within a member function, but not otherwise. Even though it's not a member.

Argument-dependent lookup is about the only other way to call it, but that's useless to you here because both arguments are Xs, not Ys.

Your workaround is the best way to do it, and avoids this mess (as you've seen).

Community
  • 1
  • 1
Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • So it's in the standard. But *why*? – Marc Mutz - mmutz Jul 02 '15 at 11:55
  • @MarcMutz-mmutz because it makes *sense*. Things have a scope, so you don't suddenly wreak havoc in places you haven't touched. If you want something to happen inside a class' scope only, you'd do that inside that class. If you wanted to do something globally, you'd do it outside. – Marcus Müller Jul 02 '15 at 11:57
  • @MarcusMüllerꕺꕺ: Scope is usually defined by the declaration, not the definition. If you define a function at some namespace scope `NS`, and then define it in an unnamed namespace within `NS`, you have created two names, not one. If, otoh, you declare a friend function in class `NS::C`, then define it in `NS`, you have only one name. To be clear: I can see why having a friend function declared or defined in `NS::C` should be in the scope of `NS::C`, but I don't see sense in the definition of said friend suddenly changing the scope. You could always grant friendship to `NS::func()` instead. – Marc Mutz - mmutz Jul 02 '15 at 12:12
  • @MarcMutz-mmutz: I would tend to agree. I don't think there's any non-confusing way for friend functions defined this way to work; it's a non-member function, defined inline in the class definition. Either way you have it, there are going to be consistencies with all other function definitions. They decided that this was, on balance, the lesser of two evils, I guess? – Lightness Races in Orbit Jul 02 '15 at 12:17
  • @MarcMutz-mmutz: I'm more than willing to agree with Lightness Races in Orbit, since I see your point. – Marcus Müller Jul 02 '15 at 12:18
0

I'm pretty sure this is a mandated standard. Here at the University I attend whenever we used friend functions it was always outside of the class. I'm not 100% sure why myself but I do know that it works without causing problems.

ShadowManes
  • 68
  • 10