Hidden friends: declarations and definitions
Asked Answered
T

2

13

In his recent blog post Anthony Williams talks about hidden friends. The main idea, if I understood it correctly, is that functions declared as friends cannot be found by ADL in certain situations. Simple example:

namespace N {
    struct A {
        friend void foo(A) { }
    };

    struct B {
        operator A();
    };

    // (*)
    void bar(A) { }
}

void func() {
    N::A a;
    bar(a);   // OK, bar is found via ADL
    foo(a);   // OK, foo is found via ADL

    N::B b;
    bar(b);   // OK, bar is found via ADL
    foo(b);   // NOT OK, foo cannot be found
}

In all examples in the blog post, the friend functions are defined inside classes. Is it possible to declare a friend function and then define it later at the point (*), so that it remains hidden? It looks like hidden friends can only be defined in the class scope (or in another compilation unit).

Toilworn answered 27/6, 2019 at 16:57 Comment(2)
As you say yourself. They have to be in the class scope or in another compilation unit, so you can but them in a source file, but then within that source file you don't get the benefit of the hidden friend feature, only other source files does. Which is why it better to just always have in inline. The whole trick revolves around the only declaration available is one within the class scope, a definition outside would be a second declaration outside of class scope ruining the effect.Trimurti
If you want to hide the implementation of a hidden friend, then simply have it forward to a private member function, then put that implementation wherever you want.Ailurophobe
T
9

Hidden friends need to be fully defined inline, i.e., inside of the definition of class. Yes, if you define friends somewhere else, where definition can cause namespace visibility, it would break restrictions based on hidden friends to be found as match by ADL search only, and as such be candidate for overload resolution.

Further more in WG21 recommendations for specifying hidden friends, is noted that hidden friends are fully defined inline, like in this snippet:

#include <ostream>
#include <compare>
class C  {
    friend ostream& operator << ( ostream&, C const& )  {}
    friend auto   operator <=>( C const&, C const& )  = default;
};
Tempest answered 27/6, 2019 at 18:37 Comment(0)
S
12

Hidden friends can be defined out of line, but then they are not hidden in that TU. As an example, consider this header:

class A {
    // this is hidden friend defined inline
    friend int operator+(A, int) { return 0; }
    // this is hidden friend defined out of line
    friend int operator+(int, A);
    // not a hidden friend (see below)
    friend int operator+(A, A);
};
int operator+(A, A); // this is no longer hidden friend in any TU

and then a separate cpp file:

#include "a.hpp"

// This is not a hidden friend in this TU, but is in other TUs.
int A::operator+(int, A) {
    return 2;
}

Doing this is useful when you have operators/ADL customization points that depend on other large headers for their implementation.

Scuttle answered 12/6, 2020 at 13:46 Comment(0)
T
9

Hidden friends need to be fully defined inline, i.e., inside of the definition of class. Yes, if you define friends somewhere else, where definition can cause namespace visibility, it would break restrictions based on hidden friends to be found as match by ADL search only, and as such be candidate for overload resolution.

Further more in WG21 recommendations for specifying hidden friends, is noted that hidden friends are fully defined inline, like in this snippet:

#include <ostream>
#include <compare>
class C  {
    friend ostream& operator << ( ostream&, C const& )  {}
    friend auto   operator <=>( C const&, C const& )  = default;
};
Tempest answered 27/6, 2019 at 18:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.