Search code examples
c++c++11c++14usingfriend

using-declaration for friend function


In C++11 it is possible to make a public member of a private base class accessible to the outside (public) with a using declaration. For example

class A {
private:
    int i = 2;
public:
    void f() { i = 3; }

    friend bool operator==(const A& l, const A& r) { return l.i == r.i; }
};

class B : private A {
public:
    using A::f;
};

int main() {
    B b, b2;
    b.f();
}

b.f() is possible because of the using A::f in the definition of B.

Is it possible write a similar declaration which would make the up-cast from B& to A& possible for the friend function operator==(A&, A&), so that b == b2 can be called in main()?


Solution

  • No, only B can internally cast itself to A, and it otherwise is not possible because from a client's perspective B is not an A but rather has an A

    Even if you replaced your friend bool operator= with a member function equals:

    class A {
    private:
        int i = 2;
    public:
        void f()  { i = 3; }
    
        bool equals(const A& r){return i == r.i;}
    };
    
    class B : private A {
    public:
        using A::f;
        using A::equals; 
    };
    

    While this compiles, you cannot ever call b.equals(b2) because no implicit conversion is ever possible from a type of B to a type of A from the caller's perspective (due to private inheritance) .

    You'll need to provide your own operator== or change your inheritance to public or protected. Here's an example where B declares its own friend bool operator==

    class B : private A {
    public:
        using A::f;
        friend bool operator==(const B& l, const B& r)
        {
            return (static_cast<A>(l) == static_cast<A>(r)) && true; 
            // "true" is a stand-in for some other condition
        }
    };
    

    Read more at isocpp


    Edit: If you really want to play games, you will notice that I said no implicit conversion is ever possible, but some explicit conversions are. Because B does technically derive from A you can do pointer casting to make it work, but I don't recommend it:

    class A {
    private:
        int i = 2;
    public:
        void f()  { i = 3; }
    
        bool equals(const A* r){return i == r->i;}
    };
    
    class B : private A {
    public:
        using A::f;
        using A::equals;
    };
    
    int main() {
        B b, b2;
        b.f();
        (::A*)(&b)->equals((::A*)(&b2));  
    }
    

    Or you could use pointer casting's ugly cousin, reference casting, if you wish to keep the original operator== syntax

    class A {
    private:
        int i = 2;
    public:
        void f()  { i = 3; }
    
        friend bool operator==(const A& l, const A& r) { return l.i == r.i; }
    };
    
    class B : private A {
    public:
        using A::f;
    };
    
    int main() {
        B b, b2;
        b.f();
        ((::A&)(b)) == ((::A&)(b2));  
    }
    

    See §11.2 [class.access.base] for more