简体   繁体   中英

How to define a friend function declared in a non template class internal to a template class outside of both classes?

I found "how to define a friend template function of a template class outside of its declaration" ( SO / cppreference ), but how to do that if we add another internal non template class in the mix?

Ie how to (externally) define operator<< declared in class Internal from the following example:

#include <iostream>

template <typename T>
class External {
public:
    explicit External(T initial) : value{initial} {}
    class Internal {
    public:
        Internal(const External& e) : internal_value{e.value} {}

    private:        
        friend std::ostream& operator<<(std::ostream& os, const Internal& i);
        // ^^^ this one
        /* body
        {
            return os << i.internal_value;
        }
        */

        T internal_value;
    };

    friend std::ostream& operator<<(std::ostream& os, const External& e)
    {
        return os << Internal{e};
    }
private:
    T value;
};

int main()
{
    std::cout << External<int>{5};
}

Here's the issue. Despite the fact that External is a template and Internal is a dependent type. The friend function is not templated itself. Odd as it may seem, it doesn't depend on the template parameter.

When you define it inline, then each specialization of the template creates the associated friend function as well. But when it's not inline you need to provide the definition of the operator for each specialization explicitly . And you can't do that with a template, since the function is not a template.

So if you add this after the template declaration:

std::ostream& operator<<(std::ostream& os, External<int>::Internal const& i)
{
    return os << i.internal_value;
}

It will build. And as you can see, only concrete types are being used in the function.

Obviously that isn't much of a solution. Any sensible person would want specializations of External to generate the friend definition as well. The way to accomplish that in a maintainable fashion is to keep the operator definition inline , but instead of doing the work there, delegate to a member function (which is dependent on the template parameter):

class Internal {
public:
    Internal(const External& e) : internal_value{e.value} {}

private:
    std::ostream& print(std::ostream&) const;
    friend std::ostream& operator<<(std::ostream& os, Internal const& i)
    {
        return i.print(os); // Short and sweet on account of being inline
    }

    T internal_value;
};

//....

template<typename T>
std::ostream& External<T>::Internal::print(std::ostream& os) const {
   // Provided outside of the class definition, can be as verbose as you like
   return os << internal_value;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM