Search code examples
c++templatesoperator-overloadingoverloadingfriend

Can't access private member in templated overloaded operator


In this code, why is it not possible to access the private field of my class in the operator overload ?

(Note that this is only a MRE, not the full code)

template <typename T>
class Frac

template <typename T, typename U>
Frac<T> operator+ (Frac<T> lhs,const Frac<U>& rhs);

template <typename T, typename U>
bool operator==(const Frac<T>& lhs, const Frac<U>& rhs);

template <typename T>
class Frac {

    template<typename>
    friend class Frac;

    friend Frac operator+ <>(Frac lhs,const Frac& rhs);

    friend bool operator== <>(const Frac& lhs, const Frac& rhs);

    private:
        T numerator, denominator;
};

template <typename T, typename U>
Frac<T> operator+(Frac<T> lhs,const Frac<U>& rhs) {
    lhs.denominator += rhs.denominator;
    return lhs;
}

template <typename T, typename U>
bool operator==(const Frac<T>& lhs, const Frac<U>& rhs) {
    return (lhs.numerator == rhs.numerator && lhs.denominator == rhs.denominator);
}

When I compile the compiler tells me that it is not possible to access the denominator and numerator fields because they are private. However the overload is indicated as friendly. The class is also indicated as friendly so that all instances of the class whatever the type are friendly.

Could someone explain me what the problem is and how to solve it?


Solution

  • To make each instance of

    template <typename T, typename U>
    bool operator==(const Frac<T>& lhs, const Frac<U>& rhs);
    

    a friend, you need to be just as verbose in your friend declaration. Copy this declaration and stick "friend" in it. There are two quirks. First, template has to come before friend, so you'll be adding the keyword in the middle of the declaration. Second, T is already being used as the template parameter to the class, so you should choose a different identifier to avoid shadowing (I'll use S).

        template <typename S, typename U>
    //                    ^^^
        friend bool operator==(const Frac<S>& lhs, const Frac<U>& rhs);
    //  ^^^^^^                           ^^^
    

    Without this change, you are saying that the friend of Frac<T> is an operator that takes two Frac<T> parameters (the same T).