c++friend-function

C++ friend operator definition inside class body serves as function declaration?


I'm a newbie reading the C++ Primer book. It says:

A friend declaration only specifies access. It is not a general declaration of the function. If we want users of the class to be able to call a friend function, then we must also declare the function separately from the friend declaration. To make a friend visible to users of the class, we usually declare each friend (outside the class) in the same header as the class itself.

But I just found that this is not the case for friend operator functions defined inside the class body. In the following code, f could not be found but operator+ is found:

struct X
{
    friend void f()
    {
        // friend functions can be defined in the class
        // this does NOT serve as a declaration, even though this is already a definition
        // to use this function, another declaration is REQUIRED
    }

    friend X operator+(const X & x1, const X & x2)
    {
        // this stuff serves as declaration somehow
        return {x1.v + x2.v};
    }

    void foo()
    {
        f();                    // ERROR: no declaration for f
        X tmp = X {1} + X {2};  // CORRECT
    }

    int v;
};

Could someone tell me what makes this difference? I am using g++ (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0, as a reference. I have searched many questions involving friend declaration and definition on StackOverflow but did not find an answer on this. Sorry if this question is duplicated.


Solution

  • Even if we define the function inside the class, we must still provide a declaration outside of the class itself to make that function visible. A declaration must exist even if we only call the friend from members of the friendship granting class. This means in your example, you should forward declare the function f as shown below:

    //forward declare f
    void f();
    
    struct X
    {
        //no ADL here since this function f has no parameters of type X. So we also need to declare this outside struct X which we did by providing forward declaration
        friend void f()
        {
            
        }
    
        void foo()
        {
            f();                    // WORKS NOW because of the forward declaration
            X tmp = X {1} + X {2};  //operator+ found using ADL
        }
        //other members here
    };
    
    

    The output of the program can be seen here.

    The reason why the call to operator+ works is because of ADL which stands for argument dependent lookup. In particular, operator+ can be found using ADL since it has parameters of type X. So in this case, you do not need to explicitly declare operator+ outside struct X.