Reputation: 4990
I tried to separate the declaration and definition of my templated member function of a templated class, but ended up with the following error and warning.
template <typename I>
class BigUnsigned{
const size_t cell_size=sizeof(I);
std::vector<I> _integers;
public:
BigUnsigned();
BigUnsigned(I);
friend std::ostream& operator<<(std::ostream& out, const BigUnsigned& bu);
};
std::ostream& operator<<(std::ostream& out, const BigUnsigned& bu){
for (auto integer : bu._integers){
out<<integer<<std::endl;
}
return out;
}
../hw06/bigunsigned.h:13:77: warning: friend declaration 'std::ostream& operator<<(std::ostream&, const BigUnsigned&)' declares a non-template function [-Wnon-template-friend] friend std::ostream& operator<<(std::ostream& out, const BigUnsigned& bu); ^ ../hw06/bigunsigned.h:13:77: note: (if this is not what you intended, make sure the function template has already been declared and add <> after the function name here) ../hw06/bigunsigned.h:16:51: error: invalid use of template-name 'BigUnsigned' without an argument list std::ostream& operator<<(std::ostream& out, const BigUnsigned& bu){ ^ ../hw06/bigunsigned.h: In function 'std::ostream& operator<<(std::ostream&, const int&)': ../hw06/bigunsigned.h:17:28: error: request for member '_integers' in 'bu', which is of non-class type 'const int' for (auto integer : bu._integers){ ^
When I joined the declaration and definition like this, everything compiles fine.
template <typename I>
class BigUnsigned{
const size_t cell_size=sizeof(I);
std::vector<I> _integers;
public:
BigUnsigned();
BigUnsigned(I);
friend std::ostream& operator<<(std::ostream& out, const BigUnsigned& bu){
for (auto integer : bu._integers){
out<<integer<<std::endl;
}
return out;
}
};
The purpose was to print member variable _integers to cout. What might be the problem?
P.S.: Using this question I made the function free, but did not help.
Upvotes: 2
Views: 2379
Reputation: 206557
A refinement to the answer by NathanOliver.
With the other answer, all instantiations of the function template are friend
s of all instatiations of the class template.
operator<< <int>
is a friend
of BigUnsigned<int>
as well as BigUnsigned<double>
.
operator<< <double>
is a friend
of BigUnsigned<double>
as well as BigUnsigned<FooBar>
.
You can change the declarations a little bit so that
operator<< <int>
is a friend
of BigUnsigned<int>
but not of BigUnsigned<double>
.
operator<< <double>
is a friend
of BigUnsigned<double>
but not BigUnsigned<FooBar>
.
// Forward declaration of the class template.
template <typename I> class BigUnsigned;
// Forward declaration of the function template
template <typename I>
std::ostream& operator<<(std::ostream& out, const BigUnsigned<I>& bu);
// Change the friend-ship declaration in the class template.
template <typename I>
class BigUnsigned{
const size_t cell_size=sizeof(I);
std::vector<I> _integers;
public:
BigUnsigned();
BigUnsigned(I);
// Grant friend-ship only to a specific instantiation of the
// function template.
friend std::ostream& operator<< <I>(std::ostream& out, const BigUnsigned<I>& bu);
};
Upvotes: 4
Reputation: 39089
To add a third variant that improves the readability a little bit, is to define the friend function inside the class:
#include <iostream>
template <typename T>
class Foo {
int test = 42;
// Note: 'Foo' inside the class body is basically a shortcut for 'Foo<T>'
// Below line is identical to: friend std::ostream& operator<< (std::ostream &os, Foo<T> const &foo)
friend std::ostream& operator<< (std::ostream &os, Foo const &foo) {
return os << foo.test;
}
};
int main () {
Foo<int> foo;
std::cout << foo << '\n';
}
Upvotes: 1
Reputation: 180415
BigUnsigned
is a template type so
std::ostream& operator<<(std::ostream& out, const BigUnsigned& bu)
Will not work as there is no BigUnsigned
. You need to make the friend function a template so you can take different types of BigUnsigned<some_type>
s.
template <typename I>
class BigUnsigned{
const size_t cell_size=sizeof(I);
std::vector<I> _integers;
public:
BigUnsigned();
BigUnsigned(I);
template<typename T>
friend std::ostream& operator<<(std::ostream& out, const BigUnsigned<T>& bu);
};
template<typename T>
std::ostream& operator<<(std::ostream& out, const BigUnsigned<T>& bu){
for (auto integer : bu._integers){
out<<integer<<std::endl;
}
return out;
}
The reason the second example works is that since it is declared inside the class it uses the template type that the class uses.
Upvotes: 7