Search code examples
c++friendidioms

Hidden Friend Concept in C++


I'm still a beginner in C++ trying to learn more about the language. I recently read about the concept of ADL (Argument-Dependent Lookup) and Hidden Friends idiom (https://www.modernescpp.com/index.php/argument-dependent-lookup-and-hidden-friends). My understanding of ADL is that in the case of an unqualified function call, C++ looks for the function in not only the current namespace, but also the namespace of the argument type.

I'm confused at what the point of the hidden friend idiom is, and what hidden friend means exactly (i.e. what is hidden about it). I get that friend functions of a class are non-member functions but can access private members of the class. However, I don't see why they are necessary. In the code example given in the reading, it points out the necessity of friends in the given functions specifically for general overloads with two parameters of a custom class. That is, in

class MyDistance{
  public:
    explicit MyDistance(double i):m(i){}

    MyDistance operator +(const MyDistance& a, const MyDistance& b){
        return MyDistance(a.m + b.m);
    }
    
    friend MyDistance operator -(const MyDistance& a, const MyDistance& b){
        return MyDistance(a.m - b.m);
    }
    
    friend std::ostream& operator<< (std::ostream &out, const MyDistance& myDist){
        out << myDist.m << " m";
        return out;
    }

  private:
    double m;

};

The + operator overload for the class is not a friend, is a member function, and technically takes in 3 parameters of MyDistance here I believe since it is a member function (this) and takes 2 additional parameters, making it invalid.

However, instead of having a hidden friend, couldn't we just write the code as

class MyDistance{
  public:
    ...
    
    MyDistance operator +(const MyDistance& other){
        return MyDistance(m + other.m);
    }
    ...
};

Is there any downside to writing the code like this? Is it slower (at compile time) in some way due to the order in which C++ does the lookup (perhaps looking at non-member functions before looking at member functions)? Also, what exactly is the "hidden friend idiom" supposed to "hide"? Is it that the function itself is defined in the class instead of outside?


Solution

  • Is there any downside? Yes, in your example above C++ applies different rules to the two arguments of operator+. Specifically the left hand argument must be an object of type MyDistance but the right hand argument can be any type convertible to MyDistance.

    Extending your example a little

    class MyDistance{
      public:
        ...
        MyDistance(int dist) { ... }
    
        MyDistance operator+(const MyDistance& other) const {
            return MyDistance(m + other.m);
        }
        ...
    };
    

    With this code

    MyDistance x(1);
    MyDistance y = x + 2;
    

    is legal because there is a conversion from int to MyDistance but this is illegal

    MyDistance x(1);
    MyDistance y = 2 + x;
    

    because given the declaration above the left hand side of + must be a MyDistance object.

    There is no such problem when operator+ is a friend, in that case either argument can be convertible to MyDistance and both versions of the code above are legal.

    Our expectation of operator+ is that it is symmetric, so the friend version is better because it applies the same rules to both arguments.