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?
I.e. 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;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With