Operator overloading for nested struct only working as member or friend function

This C++ code compiles and runs perfectly, as I expect:

template <typename T>  struct S { T *p; };

template <typename T>
bool operator == (S<T> &a, S<T> &b) { return a.p == b.p; }

int main () { int i;  S<int> a = {&i}, b = {&i};  return a == b; }

However, if I try to do the same with the inner struct of an outer struct...

template <typename T>  struct O {  struct I {T *p;};  };

template <typename T>
bool operator == (O<T>::I &a, O<T>::I &b) { return a.p == b.p; }

int main () { int i;  O<int>::I a = {&i}, b = {&i};  return a == b; }

... then it doesn't compile anymore (gcc version 8.3.0, Debian GNU/Linux 10):

1.cpp:4:25: error: declaration of ‘operator==’ as non-function
 bool operator == (O<T>::I &a, O<T>::I &b) { return a.p == b.p; }

Why is it so? I also do not understand the above error message.

Note that I'm aware that I can make it work by defining the operator as a member function of the inner struct:

template <typename T>
struct O2 {
           struct I2 {
                      T *p;

                      bool operator == (I2 &b) { return p == b.p; }

int main () { int i;  O2<int>::I2 a = {&i}, b = {&i};  return a == b; }

However, if somehow possible, I'd rather use the non-member function version, because I find it more symmetric and therefore clearer.

Also, partly by trial and error, I found that the following symmetric version works...

template <typename T>
struct O3 {
           struct I3 { T *p; };

           friend bool operator == (I3 &a, I3 &b) { return a.p == b.p; }

int main () { int i;  O3<int>::I3 a = {&i}, b = {&i};  return a == b; }

... but I do not really understand what is happening above. First, given that a friend declaration "grants a function or another class access to private and protected members of the class where the friend declaration appears", I do not understand how it helps in the code above, given that we're always dealing with structs and therefore with public members.

Second, if I remove the friend specifier, then it doesn't compile anymore. Also, the [...] operator== [...] must have exactly one argument error message makes me think that in this case the compiler expects me to define a member function operator== whose left operand is O3, not I3. Apparently, however, the friend specifier changes this situation; why is it so?

Read more here: https://stackoverflow.com/questions/65710351/operator-overloading-for-nested-struct-only-working-as-member-or-friend-function

Content Attribution

This content was originally published by Pablo M. S. Farias at Recent Questions - Stack Overflow, and is syndicated here via their RSS feed. You can read the original post over there.

%d bloggers like this: