I would like to make the example below compile. I have a class template which defines a friend function template, but it's not visible in the surrounding namespace by default.
namespace ns {
template<typename T> struct Foo { // Class template.
template<typename U> friend void bar(Foo, Foo<U> f) { // Friend function template, defined inline.
static_cast<void>(f.private_field); // Should only compile when T=U.
}
private:
int private_field{};
};
}
int main() {
bar(ns::Foo<char>{}, ns::Foo<char>{}); // Ok.
ns::bar(ns::Foo<char>{}, ns::Foo<char>{}); // (1) FIXME: `ns::bar` not found.
//bar(ns::Foo<bool>{}, ns::Foo<char>{}); // (2): Should fail because bar() here is not a friend of Foo<char>.
}
I would like to be able to call ns::bar<int>(ns::Foo<char>{})
((1)
should compile) while not declaring any friendship between bar
call and unrelated Foo<char>
at (2)
. How do I make this happen?
If Foo
was a non-template, I would declare template<typename U> void bar(Foo, Foo);
in namespace ns
.
If bar
was a non-template (e.g. with U=char
fixed), I would declare it as a template function outside the class and befriend its full specialization, like here: friend void bar<T>(Foo, Foo<char> f);
.
However, both are templates and I'm out of ideas.
UPD: I've tried giving the same trick as with non-template bar
and make it a template outside of the class. However, it looks like befriending a partial specialization is not possible.
My attempt:
namespace ns {
template<typename T> struct Foo;
template<typename U, typename T> void bar(Foo<T>, Foo<U>);
template<typename T> struct Foo {
template<typename U> friend void bar<U, T>(Foo, Foo<U> f);
// template<typename U, typename TT> friend void bar(Foo<TT>, Foo<U> f); // Compiles, but gives extra friendship to `bar`.
private:
int private_field{};
};
template<typename U, typename T> void bar(Foo<T>, Foo<U> f) {
static_cast<void>(f.private_field); // Should only compile when T=U.
}
}
int main() {
bar(ns::Foo<char>{}, ns::Foo<char>{}); // Ok.
ns::bar(ns::Foo<char>{}, ns::Foo<char>{}); // (1) FIXME: `ns::bar` not found.
//bar(ns::Foo<bool>{}, ns::Foo<char>{}); // (2): Should fail because bar() here is not a friend of Foo<char>.
}
GCC's output:
x.cpp:7:38: error: invalid use of template-id 'bar<U, T>' in declaration of primary template
7 | template<typename U> friend void bar<U, T>(Foo, Foo<U> f);
| ^~~~~~~~~
x.cpp: In instantiation of 'void ns::bar(ns::Foo<T>, ns::Foo<U>) [with U = char; T = char]':
x.cpp:19:41: required from here
x.cpp:14:25: error: 'int ns::Foo<char>::private_field' is private within this context
14 | static_cast<void>(f.private_field); // Should only compile when T=U.
| ~~^~~~~~~~~~~~~
x.cpp:10:9: note: declared private here
10 | int private_field{};
| ^~~~~~~~~~~~~