Skip to content

Latest commit

 

History

History
204 lines (139 loc) · 4.73 KB

may30_hidden_friends.md

File metadata and controls

204 lines (139 loc) · 4.73 KB

Hidden Friend Functions

TL;DR

Hidden friend functions are current best practice for defining operators on your classes.


CPPIBAA - C++ Is Bad At Abbreviations
CPPIBAN - C++ Is Bad At Naming

Note: the "hidden" in hidden friend functions has little/nothing to do with data hiding using private member variables. Just like C++ overuses keywords (like static) to mean different things, it also overuses terminology to mean different things.

So What are hidden friend functions

Hidden friend functions are friend functions that are defined (not just declared) inside the class:

Hidden Not Hidden
namespace N
{

class C
{
    int x;
    int y;
        
public:
    // this is a hidden friend function
    friend operator<(C const & a, C const & b)
    {
        return std::tie(a.x, a.y)
            < std::tie(b.x, b.y);
    }
};








} // namespace N
namespace N
{

class C
{
    int x;
    int y;
        
public:
    // this is NOT a hidden friend function
    friend operator<(C const & a, C const & b);




};
    
// definition outside class makes it not hidden
inline C::operator<(C Const & a, C const & b)
{
   return std::tie(a.x, a.y)
        < std::tie(b.x, b.y);
}

} // namespace N

OK, so now I know what a hidden friend function is. But...

What's the difference (relative to non-hidden friend functions)?

A friend function (hidden or not) is injected into the enclosing namespace (ie N above). (It is not in the scope of C.) However, this does NOT make it available for qualified and unqualified lookup (ie lookup when the compiler tries to figure out what function/operator is being called). The declaration only makes the name available for ADL (argument dependent lookup - ie lookup based on the arguments).

If the definition is placed outside the class, then it becomes available for qualified/unqualified lookup.

So if the definition is inside the class, it is only available via ADL and not qualified/unqualified lookup.

OK, but...

What's the point?

When a compiler sees a + b it needs to find the right + operator. It does that by building up a list of all available + operators. There can be a lot of them. For the << operator, a lot of them. From that list of potential candidates, it picks the best match.

Hidden friend functions are found less. Or, better yet, considered less. They do not show up in the list of candidates as often. This makes compile times faster, and makes compiler errors shorter (compilers often show the entire list of candidates when it can't determined the best match).

What about the cases where the hidden friends are not found, but you wanted them to be found?

No you didn't.

C++ name lookup is the wild west. Asking the compiler to "do the right thing" is great, until it isn't. Some day in the future it will pick the "wrong" candidate, and you will be left scratching your head.

P.S. Non-member Operators are Better than Member Operators except when they are not

For binary operators, you have two choices:

member Non-member
namespace N
{

class C
{
    int x;
    int y;
        
public:
    // this is a member operator
    operator<(C const & b)
    {
        return std::tie(this->x, this->y)
            < std::tie(b.x, b.y);
    }
};

} // namespace N
namespace N
{

class C
{
    int x;
    int y;
        
public:
    // this is a non-member operator (whether friend or defined outside the class)
    friend operator<(C const & a, C const & b)
    {
        return std::tie(a.x, a.y)
            < std::tie(b.x, b.y);
    }
};

} // namespace N

Typically the non-member version is better - When using a member operator, a call like d < c only works when d has the operator. With non-member, it also works when d and/or c has a conversion to something with the non-member operator.

But wait!

C++20 now has space-ship operator <=> and a new set of rules for the related operators. For those, guess what!? - Member operator is what you want. The new "rewrite rules" make it work just as well or better. Honestly, I forget why it is better now, but it is right in the paper on space-ship...


See also, http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1601r0.pdf by Walter E. Brown and Daniel Sunderland