I am struggling with friend statement for a templated operator and namespaces. Sorry if I'm a bit long but I want to give a good description of my issue.
First, some context. Forget about the namespace at present.
I have a class A
and a public operator that needs to access its private member:
template<typename U>
struct B { U valb; };
template<typename U>
struct C { U valc; };
template<typename U,typename V>
struct A
{
private:
U v1; V v2;
template<typename T1,typename T2>
friend A<T1,T2> operator * ( const B<T2>&, const C<T1>& );
};
template<typename T1,typename T2>
A<T1,T2>
operator * ( const B<T2>& b, const C<T1>& c )
{
A<T1,T2> a;
a.v1 = c.valc * b.valb; // dummy
return a;
}
int main()
{
B<float> b;
C<int> c;
auto a = b * c;
}
This builds fine.
Now for some reason I want to put class A
in a namespace, (mostly to remove it from the public API, user code will use "sub" types, declared with a using
declaration).
Now the trouble begins.
I am building on this answer, that covers that topic and works fine.
It explains that I need to forward declare the class, then the operator, and in the friend declaration, prefix the operator with ::
.
The only difference between the situation described in that linked question and mine is the return type. In my case, it is a templated type. This seems to be the trouble (or is it?)
So I tried that (online here):
template<typename U>
struct B { U valb; };
template<typename U>
struct C { U valc; };
// forward declaration of class A
namespace ns {
template<typename U,typename V> struct A;
}
// forward declaration of operator
template<typename T1,typename T2>
ns::A<T1,T2>
operator * ( const B<T2>&, const C<T1>& );
namespace ns {
template<typename U,typename V>
struct A // class declaration
{
template<typename T1,typename T2>
friend A<T1,T2> ::operator * ( const B<T2>&, const C<T1>& );
private:
U v1; V v2;
};
} // namespace
// operator definition
template<typename T1,typename T2>
ns::A<T1,T2> operator * ( const B<T2>& b, const C<T1>& c )
{
ns::A<T1,T2> a;
a.v1 = c.valc * b.valb; // dummy
return a;
}
int main()
{
B<float> b;
C<int> c;
auto a = b * c;
}
This fails to build with:
error: ISO C forbids declaration of 'operator*' with no type [-fpermissive]
23 | friend A<T1,T2> ::operator * ( const B<T2>&, const C<T1>& );
And if I remove the ::
, then the operator is not recognized as a friend.
What am I doing wrong? How can I manage that issue?
CodePudding user response:
Unless the befriend function template is declared already, I don't think you define this function template outside the class definition: essentially, there is no way to actually name the operator. To make things a bit more interesting, the two parameter types operated on are actually in a different namespace, i.e., the operator*()
really needs to be defined in a different namespace than ns
. It seems this does the trick:
template<typename U> struct B { U valb; };
template<typename U> struct C { U valc; };
// declaration of ns::A to declare the operator*
namespace ns { template<typename U,typename V> struct A; }
template<typename T1,typename T2>
ns::A<T1,T2> operator * ( const B<T2>&, const C<T1>& );
namespace ns {
template<typename U,typename V>
struct A {
template<typename T1,typename T2>
friend auto ::operator * ( const B<T2>&, const C<T1>& ) -> A<T1, T2>;
private:
U v1; V v2;
};
}
template<typename T1,typename T2>
ns::A<T1,T2> operator * ( const B<T2>& b, const C<T1>& c ) {
ns::A<T1,T2> a;
a.v1 = c.valc * b.valb; // dummy
return a;
}
int main()
{
B<float> b;
C<int> c;
auto a = b * c;
}
For a live demo see this Compiler Explorer link.