In the following example I try to access the private member function subscribe()
from a templated class type from within its friend function. However it appears that the friend function is only befriended in one instantiation of the class and not the other, hence yielding a "private within this context"-error. Check out the following:
#include <cstdio>
template <typename T>
class my_class
{
template <typename... Ts>
friend auto observe(my_class<Ts>... args) {
([&]{ args.subscribe(); }(), ...);
}
void subscribe() {
printf("subscribed!\n");
}
};
int main()
{
my_class<int> a{};
my_class<bool> b{};
observe(a,b);
}
Error:
<source>:19:20: required from here
<source>:7:17: error: redefinition of 'template<class ... Ts> auto observe(my_class<Ts>...)'
7 | friend auto observe(my_class<Ts>... args) {
| ^~~~~~~
<source>:7:17: note: 'template<class ... Ts> auto observe(my_class<Ts>...)' previously declared here
<source>: In instantiation of 'auto observe(my_class<Ts>...) [with Ts = {int, bool}; T = int]':
<source>:20:12: required from here
<source>:8:29: error: 'void my_class<T>::subscribe() [with T = bool]' is private within this context
8 | ([&]{ args.subscribe(); }(), ...);
| ~~~~~~~~~~~~~~^~
<source>:11:10: note: declared private here
11 | void subscribe() {
| ^~~~~~~~~
Is this actually correct by the compiler?
Yes, this is correct.
my_class<int>
and my_class<bool>
are separate classes, and everything inside them will get defined separately for each instantiation of the template. Normally this is fine, because the stuff inside the classes is class members, so i.e. my_class<int>::subscribe
and my_class<bool>::subscribe
are different functions.
But observe
is not a class member, so the fact that it gets defined by both my_class<int>
and my_class<bool>
is a violation of the One Definition Rule.
What you need to do is move the definition of observe
outside of my_class
so that it only gets defined once:
#include <cstdio>
template <typename T>
class my_class
{
public:
template <typename... Ts>
friend auto observe(my_class<Ts>... args);
void subscribe() {
printf("subscribed!\n");
}
};
template <typename... Ts>
auto observe(my_class<Ts>... args) {
([&]{ args.subscribe(); }(), ...);
}
int main()
{
my_class<int> a{};
my_class<bool> b{};
observe(a,b);
}