I am wondering how to make a function friend of a class and define the function outside class if the function's template arguments include but are not limited to the class's template arguments.
For example, I have the following template class and template friend function:
template<int N> class Matrix;
template<typename T, int N> Matrix<N> operator*(const Matrix<N> &m1, const T &m2);
// class definition
template<int N>
class Matrix{
template<typename T>
friend Matrix<N> operator* (const Matrix<N> &m1, const T &m2);
};
// friend function definition
template<typename T, int N> Matrix<N> operator*(const Matrix<N> &m1, const T &m2)
{
return m1; // just as an example
}
If I compile:
Matrix<3> m;
m * 1.0;
I would get the following linker error:
test.cc:(.text+0x1c7): undefined reference to `Matrix<3> operator*<double>(Matrix<3> const&, double const&)'
collect2: error: ld returned 1 exit status
You have a mismatch in kind.
Your initial declaration, and later definition, have this signature:
template<typename T, int N>
Matrix<N> operator*(const Matrix<N> &m1, const T &m2);
This is a function template taking two template parameters: T and N.
However, within your class, you make as a friend a function template that has this signature:
template<typename T>
friend Matrix<N> operator* (const Matrix<N> &m1, const T &m2);
This only has one template parameter: T. N is fixed here. This friend declaration also declares this function template. This is a better match, but not actually defined, hence the behavior you see.
You have two options I think.
Remove the namespace-scope declaration of operator* and just declare and define the friended operator* within the definition of Matrix.
Change the friend declaration to match the namespace-scope declaration:
template<typename T, int M>
friend Matrix<M> operator* (const Matrix<M> &m1, const T &m2);
(1) is the better option typically - doesn't involve adding more operator*s into global scope, which is good for compile times.
Related
I'm trying to write my own vector template class, but I have some problems when writing friend function declarations.
At first I wrote like this:
template <typename T, typename Alloc = std::allocator<T>>
class vector {
public:
friend bool operator==(const vector<T, Alloc>&, const vector<T, Alloc>&);
};
But the compiler reports a warning that I declare a non-template function. So I changed the friend declaration to this:
template <typename T, typename Alloc = std::allocator<T>>
class vector {
public:
template <typename E, typename F>
friend bool operator==(const vector<E, F>&, const vector<E, F>&);
};
So far everything is fine, but I think there are still problems. If I write like that, I make all operator== functions which take two template arguments as its friend functions. For example, operator==(const vector<int>&, const vector<int>&) and operator==(const vector<double>&, const vector<double>&) would both be vector<int>'s friend function.
What is the right way to write friend functions in template class?
Friend non-template function
But the compiler reports a warning that I declare a non-template function.
Yes, you're declaring a non-template function inside the class definition. That means if you define it out of the class definition, you have to define it as non-template function, and for all the possible instantiations , like:
bool operator==(const vector<int>& v1, const vector<int>& v2)
{
...
}
bool operator==(const vector<char>& v1, const vector<char>& v2)
{
...
}
That is ugly, you can define it inside the class definition like
template <typename T, typename Alloc = std::allocator<T>>
class vector {
public:
friend bool operator==(const vector<T, Alloc>&, const vector<T, Alloc>&) {
...
}
};
Friend function template
If you want to define it as template function, and constrain the scope of friendship, you can
// forward declaration
template <typename T, typename Alloc>
class vector;
// forward declaration
template <typename T, typename Alloc>
bool operator==(const vector<T, Alloc>& v1, const vector<T, Alloc>& v2);
template <typename T, typename Alloc = std::allocator<T>>
class vector {
private:
int i;
public:
// only the instantiation of operator== with template parameter type of current T and Alloc becomes friend
friend bool operator==<>(const vector<T, Alloc>& v1, const vector<T, Alloc>& v2);
};
template <typename T, typename Alloc = std::allocator<T>>
bool operator==(const vector<T, Alloc>& v1, const vector<T, Alloc>& v2)
{
...
}
Then, for vector<int>, only bool operator==(const vector<int>&, const vector<int>&) is friend, other instantiations like bool operator==(const vector<double>&, const vector<double>&) is not.
LIVE
In The C++ Programming Language, Fourth Edition - chapter 23.4.7 Friends, I found following example (I have slightly modified it to show only relevant part):
template<typename T>
class Vector {
public:
friend Vector operator*<>(const Vector& v, int f);
^^ ~~~~ ?
};
template<typename T>
Vector<T> operator*(const Vector<T>& v, int f) {
return v;
}
I tried to compile it, but I get following error (clang):
main.cpp:8:20: error: friends can only be classes or functions
friend Vector operator*<>(const Vector& v, int f);
^
main.cpp:8:29: error: expected ';' at end of declaration list
friend Vector operator*<>(const Vector& v, int f);
^
;
2 errors generated.
Book explains that :
The <> after the name of the friend function is needed to make clear that the friend is a template function. Without the <>, a non template function would be assumed.
And that is all on this.
Without <> this code compiles, but when operator* is used (ex.: Vector<int> v; v*12;) then linker error appears:
main.cpp:(.text+0xb): undefined reference to `operator*(Vector<int> const&, int)'
So I assume that <> is needed to tell compiler that function template for operator* should be generated each time Vector template is instantiated for given type.
But what am I doing wrong in the example from the book, and why?
As the book said,
the <> after the name of the friend function is needed to make clear that the friend is a template function.
That means, the name should refer to a function template, which should be declared (as template) in advance. e.g.
// forward declaration of the class template
template<typename T>
class Vector;
// declaration of the function template
template<typename T>
Vector<T> operator*(const Vector<T>& v, int f);
template<typename T>
class Vector {
public:
// friend declaration
friend Vector operator*<>(const Vector& v, int f);
};
// definition of the function template
template<typename T>
Vector<T> operator*(const Vector<T>& v, int f) {
return v;
}
In your case, you're declaring operator* as a friend directly inside Vector, without any previous declaration. Therefore the correct syntax is:
template<typename T>
class Vector {
public:
template<typename>
friend Vector operator*(const Vector& v, int f);
};
template<typename T>
Vector<T> operator*(const Vector<T>& v, int f) {
return v;
}
live example on wandbox
To make template friend method syntax work you need a forward declaration of this template method.
template<typename T>
class Vector;
template<typename T>
Vector<T> operator*(const Vector<T>& v, int f);
template<typename T>
class Vector
{
template<typename T_> friend
Vector<T_> operator*(const Vector<T_>& v, int f);
};
template<typename T>
Vector<T> operator*(const Vector<T>& v, int f)
{
return v;
}
Whatever book you're using is explaining it incorrectly.
What you need to do is
template<typename T>
class Vector
{
public:
friend Vector<T> operator*(const Vector<T>& v, int f);
};
template<typename T>
Vector<T> operator*(const Vector<T>& v, int f)
{
return v;
}
The above makes the operator*() which accepts a Vector<T> a friend of Vector<T> but not a friend of Vector<U> (unless T is the same type as U).
Within the class definition it is possible to leave out the <T> from Vector<T>, but in my experience mere human beings seem to have more trouble convincing themselves that the function declaration and function definition correspond to each other. So I generally prefer not doing that .... your call though.
The <> syntax is used when explicitly specialising templates, but that's not what you are trying to do. For example, with a templated function;
template <class T> void foo(T) { /* whatever */ }
template<> void foo<int> {/* something specific to int */ }
Using <> is what C++ FAQ suggests, too.
But you can solve it by simply using the templated declaration like you would normally, except the parameters must be named differently from the class parameters. Then in the separate definition you can again use any type names:
template <typename T>
class Vector {
public:
T i{};
// Typename must be different from the class typename(s).
template <typename T_1>
friend ostream& operator<<(ostream& os, const Vector<T_1>& v);
};
// Typename can be any.
template <typename T>
ostream& operator<<(ostream& os, const Vector<T>& v) {
return os << v.i;
}
Live demo
That's all. No need for weird <> in-between function declaration, or pre-declarations.
Assume we have a template class with friend function:
template<class T>
class A {
friend A operator+ (int, const A&);
};
This function is implemented somewhere below:
template<class T>
A<T> operator+ (int i, const A<T>& a) {
...
}
And also there is force instantiation of class template further below:
template class A<int>;
Does this imply that operator+(int, A<int>) will be compiled? Or do I have to force instantiate it separately to achieve that?
Template parameters aren't automatically forwarded to friend declarations. You need to specify a template parameter for the function as well:
template<class T>
class A {
template<class U>
friend A<U> operator+ (int, const A<U>&);
};
Implementation is almost correct, should be
template<class T>
A<T> operator+ (int i, const A<T>& a) {
// ^^^
// ...
}
I prefer to write definitions for class and function templates in a separate file which is automatically included after the "public" header. However, I've come to an interesting case where it looks like I can't do that.
template <typename T>
class Outer
{
public:
template <typename U>
class Inner
{
friend bool operator ==(const Inner& lhs, const Inner& rhs);
};
};
using Type = Outer<int>::Inner<short>;
int main()
{
Type a;
Type b;
a == b;
}
Is it possible to write definition of operator== separately that will work for any T and U?
For a particular specialization, yes:
template <typename T>
class Outer
{
public:
template <typename U>
class Inner
{
int x = 42;
friend bool operator ==(const Inner& lhs, const Inner& rhs);
};
};
using Type = Outer<int>::Inner<short>;
bool operator ==(const Type& lhs, const Type& rhs) {
return lhs.x == rhs.x;
}
int main()
{
Type a;
Type b;
a == b;
}
In your example, each specialization of the template befriends a non-template function that takes that particular specialization as parameters. You could define this function in-class (and then it will be stamped out every time the template is instantiated), or you could define it out-of-class - but then you would have to define one for every specialization you ever use.
As Igor Tandetnik points out, your example declares a non-template friend function, which you'll have to overload for each template instantiation, which is one reason friend functions are allowed to be defined inline (so they can be generated by the template without having to be templates themselves).
If you want to define the friend function as a template, here is the closest I was able to come up with:
template <typename T>
struct Outer {
template <typename U>
struct Inner;
};
template<typename T, typename U>
bool operator==( typename Outer<T>::template Inner<U> const &, typename Outer<T>::template Inner<U> const & );
template <typename T>
template <typename U>
struct Outer<T>::Inner {
friend bool operator==<T,U>(Inner const &, Inner const &);
};
template<typename T, typename U>
bool operator==( typename Outer<T>::template Inner<U> const &, typename Outer<T>::template Inner<U> const & ) {
return true;
}
// I switched this out, because my gcc-4.6 doesn't
// understand "using" aliases like this yet:
typedef Outer<int>::Inner<short> Type;
int main() {
Type a;
Type b;
operator==<int,short>( a, b );
}
Unfortunately, you'll notice the call site of this operator function is very awkward: operator==<int,short>( a, b ). I believe defining a function template on a nested class template like this disables (or at least interferes with) argument deduction, so you have to specify the template parameters explicitly (which means calling it as a function rather than in operator form). This is why the inline friend definition is so convenient. If you really want to define your operator=='s code separately, I'd recommend defining the friend inline to call another function template (with the proper template arguments), which you can then define out-of-line as a free function.
This is a pretty short snippet that just won’t compile with g++ 4.7.1 (it won’t compile either with gcc 4.6.3 by the way).
#include <iostream>
template<typename T>
struct Foo
{
template<typename U>
friend std::ostream& operator<<(Foo&, U&);
};
template<typename T, typename U>
std::ostream& operator<<(Foo<T> foo, U& u)
{
std::cout << u;
return std::cout;
}
int main()
{
Foo<int> f;
f << "bar";
return 0;
}
And this is what gcc 4.7.1 outputs (4.6.3 says almost the same thing).
/tmp/ccNWJW6X.o: In function main': main.cpp:(.text+0x15): undefined
reference tostd::basic_ostream >&
operator<< (Foo&, char const (&) [4])' collect2:
ld returned 1 exit status
Anyone could explain why?
EDIT
I also tried with clang 3.1, and it says exactly the same thing.
Friendship with templates can be a bit complicated... Lets see what your code does:
template<typename T>
struct Foo {
template<typename U>
friend std::ostream& operator<<(Foo&, U&); // [1]
};
template<typename T, typename U>
std::ostream& operator<<(Foo<T> foo, U& u) { // [2]
std::cout << u;
return std::cout;
}
When you instantiate Foo with a type, for example int the friend declaration in [1] declares a template function:
template <typename U>
std::ostream& operator<<(Foo<int>&,U&);
But that function does not exist anywhere, what you are providing in [2] is a template that takes two arguments:
template<typename T, typename U>
std::ostream& operator<<(Foo<T> foo, U& u);
The key point is that the friend declaration is processed while the template is being instantiated, and at that time Foo represents the type obtained with the current instantiation.
There are different options for what you want to do, the simplest is changing the friend declaration to:
template<typename W, typename U>
friend std::ostream& operator<<(Foo<W> foo, U& u);
Which declares a template taking two arguments (both W and U are unbound here), and matches your definition at namespace level.
Another option is defining the friend function inside the class template definition, in which case you can maintain the original signature. For more information on the different alternatives, take a look at this other answer
You did not actually write the output operator<< for Foo
Notice the signatures for the two functions are very different