Reputation: 1120
I have a template class Test
(with an integer as template argument) and a template function (in this case operator*
) that takes two objects of Test
class with possibly different template arguments. The function needs to be friend to both its arguments. Here is a minimally working example:
#include <type_traits>
template <int N>
class Test;
template <int N1, int N2>
Test<N1+N2> operator* (Test<N1>, Test<N2>);
template <int N>
class Test {
double val;
public:
Test (double x) : val{x} {}
template <int N1, int N2>
friend Test<N1+N2> operator* (Test<N1>, Test<N2>);
};
template <int N1, int N2>
Test<N1+N2> operator* (Test<N1> x, Test<N2> y) {
return Test<N1+N2> {x.val*y.val};
}
int main (int argc, char* argv[]) {
Test<1> a{4.}, c{7.9};
Test<2> b{3.5};
a*b;
a*c;
return 0;
}
This works, but the function is friend to every specializations of Test
. I want to make it to be friend only with Test<N1>
and Test<N2>
.
I tried declaring it like this:
template <int N>
class Test {
double val;
public:
Test (double x) : val{x} {}
template <int N1, int N2>
friend std::enable_if_t<N1==N||N2==N,Test<N1+N2>> operator* (Test<N1>, Test<N2>);
};
but encountered g++ errors for ambiguous overloads. I also tried:
template <int N>
class Test {
double val;
public:
Test (double x) : val{x} {}
template <int N1, int N2, typename = std::enable_if_t<N1==N||N2==N>>
friend Test<N1+N2> operator* (Test<N1>, Test<N2>);
};
but default template arguments are not allowed for friend declarations.
I prefer a solution in C++14 but solutions in C++17 would also be acceptable.
Update: Following the answer of S.M. I suggest the following solution to those with similar problems
template <int N>
class Test {
double val;
public:
Test (double x) : val{x} {}
template <int N2>
Test<N+N2> operator* (Test<N2> y) {
return Test<N+N2> {val*y.val};
}
template <int N2>
friend class Test;
};
int main (int argc, char* argv[]) {
Test<1> a{4.}, c{7.9};
Test<2> b{3.5};
a*b;
a*c;
return 0;
}
Upvotes: 3
Views: 593
Reputation: 38773
I offer the solution bellow. The function is not a friend function any more.
#include <type_traits>
template <int N>
class Test {
double val;
public:
Test (double x) : val{x} {}
template <int N2>
Test<N+N2> mul(const Test<N2> &y) const {
return Test<N+N2>{val * y.val};
}
template <int N2>
friend class Test;
};
template <int N1, int N2>
Test<N1+N2> operator* (const Test<N1> &x, const Test<N2> &y) {
return Test<N1+N2>{x.template mul<N2>(y)};
}
int main (int argc, char* argv[]) {
Test<1> a{4.}, c{7.9};
Test<2> b{3.5};
a*b;
a*c;
return 0;
}
As a member operator *
:
#include <type_traits>
template <int N>
class Test {
double val;
public:
Test (double x) : val{x} {}
template <int N2>
Test<N+N2> operator *(const Test<N2> &y) const {
return Test<N+N2>{val * y.val};
}
template <int N2>
friend class Test;
};
int main (int argc, char* argv[]) {
Test<1> a{4.}, c{7.9};
Test<2> b{3.5};
a*b;
a*c;
return 0;
}
Upvotes: 1
Reputation: 19031
No, you can't do this.
17.5.4 Friends [temp.friend]
A friend of a class or class template can be a function template or class template, a specialization of a function template or class template, or a non-template function or class.
Out of these 6 options you might be interesting in function template.
So you can befriend the operator in three ways
template <int N1, int N2>
friend Test<N1+N2> operator* (Test<N1>, Test<N2>); #1
template <int N2>
friend Test<N + N2> operator* (Test<N>, Test<N2>); #2
template <int N1>
friend Test<N1 + N> operator* (Test<N1>, Test<N>); #3
Option #1 you have tried and it doesn't work as you would like it to. Options #2 and #3 also won't work for two reasons: first, you would need to define this overload somewhere, second, if you define such a overload it would not work with second parameter (#2) and with first parameter (#3).
Upvotes: 0