Template friend in templat class

  • Thread starter Fred Zwarts \(KVI\)
  • Start date
F

Fred Zwarts \(KVI\)

I have a template class:

template <typename Base_t> class Rational_t {

// Definition of private and public members left out.
// Probably not relevant.

};

I want an output operator << for this class for different output streams.
I am not sure that I completely understand the hierarchy of the i/o stream
classes, but I have the impression that the base class in which the operator
<< is defined is a template class. (It would be much easier if the first
class in which the << operator is defined, were a non-template class.) So, I
need at least two template parameters for my << operator. Therefore, in the
same header file I had:

template <typename Base_t, typename ostream_t>
ostream_t & operator<<
(ostream_t & out, const Rational_t<Base_t> & Rat) {

// Implementation left out.
// Probably not relevant.

}

This works.

Now, I want to change the implementation of the << operator a bit, for which
it needs access to some private members of the Rational_t class. Therefore,
I want to make the << operator a friend of the class. This requires a few
forward declarations at the start of the file.

template <typename Base_t> class Rational_t;

template <typename Base_t, typename ostream_t>
ostream_t & operator<<
(ostream_t & out, const Rational_t<Base_t> & Rat);

This also still works, as long as the implementation of the << operator does
not access the private members of Rational_t.

I have tried several ways to write the friend declaration, but non of them
works.
I think the last one I tried was:

template <typename Base_t> class Rational_t {

template <typename ostream_t>
friend ostream_t& operator<<
(ostream_t& out, const Rational_t & Rat);

// Definition of private and public members left out.
// Probably not relevant.

};

When I use g++ under Linux
(g++ (SUSE Linux) 4.3.4 [gcc-4_3-branch revision 152973]),
I see a lot of messages like (One long line wrapped by me.):

TestRational.cpp:(.text+0xa93): undefined reference to
`std::basic_ostream<char, std::char_traits<char> >&
Rational::eek:perator<<
<std::basic_ostream<char, std::char_traits<char> > >
(std::basic_ostream<char, std::char_traits<char> >&,
Rational::Rational_t<int> const&)'

It seems that the compiler has no problem with the code, but the linker has.
Usually this indicates that the friend declaration is interpreted as a
forward declaration, for which no implementation is found. So apparently
something is wrong with the friend declaration. But I don't see how I can
make it work.
Any suggestion?
 
F

Fred Zwarts \(KVI\)

"Fred Zwarts (KVI)" wrote in message news:[email protected]...
I have a template class:

template <typename Base_t> class Rational_t {

// Definition of private and public members left out.
// Probably not relevant.

};

I want an output operator << for this class for different output streams.
I am not sure that I completely understand the hierarchy of the i/o stream
classes, but I have the impression that the base class in which the
operator << is defined is a template class. (It would be much easier if the
first class in which the << operator is defined, were a non-template
class.) So, I need at least two template parameters for my << operator.
Therefore, in the same header file I had:

template <typename Base_t, typename ostream_t>
ostream_t & operator<<
(ostream_t & out, const Rational_t<Base_t> & Rat) {

// Implementation left out.
// Probably not relevant.

}

This works.

Now, I want to change the implementation of the << operator a bit, for
which it needs access to some private members of the Rational_t class.
Therefore, I want to make the << operator a friend of the class. This
requires a few forward declarations at the start of the file.

template <typename Base_t> class Rational_t;

template <typename Base_t, typename ostream_t>
ostream_t & operator<<
(ostream_t & out, const Rational_t<Base_t> & Rat);

This also still works, as long as the implementation of the << operator
does not access the private members of Rational_t.

I have tried several ways to write the friend declaration, but non of them
works.
I think the last one I tried was:

template <typename Base_t> class Rational_t {

template <typename ostream_t>
friend ostream_t& operator<<
(ostream_t& out, const Rational_t & Rat);

// Definition of private and public members left out.
// Probably not relevant.

};

When I use g++ under Linux
(g++ (SUSE Linux) 4.3.4 [gcc-4_3-branch revision 152973]),
I see a lot of messages like (One long line wrapped by me.):

TestRational.cpp:(.text+0xa93): undefined reference to
`std::basic_ostream<char, std::char_traits<char> >&
Rational::eek:perator<<
<std::basic_ostream<char, std::char_traits<char> > >
(std::basic_ostream<char, std::char_traits<char> >&,
Rational::Rational_t<int> const&)'

It seems that the compiler has no problem with the code, but the linker
has.
Usually this indicates that the friend declaration is interpreted as a
forward declaration, for which no implementation is found. So apparently
something is wrong with the friend declaration. But I don't see how I can
make it work.
Any suggestion?

I forgot to mention that both the class and the << operator are in the
namespace Rational,
which is seen in the error message.
 
V

Victor Bazarov

I have a template class:

[..blah..]
Any suggestion?

Here is a suggestion: post the *simplest possible* code in such a way
that we can copy it from your message and paste it into our text editor
and hit the "build" button. The code you post should produce the exact
message you quote when compiled with the exact compiler you mention.
See FAQ 5.8. If I have to gather fragments of code from your post and
then massage it into compilation by adding some other parts like the
shit you decided wasn't important, etc., it's *too much work* for *no
pay*. Keep that in mind when asking for help, please.

V
 
F

Fred Zwarts \(KVI\)

"Victor Bazarov" wrote in message news:[email protected]...
I have a template class:

[..blah..]
Any suggestion?

Here is a suggestion: post the *simplest possible* code in such a way that
we can copy it from your message and paste it into our text editor and hit
the "build" button. The code you post should produce the exact message you
quote when compiled with the exact compiler you mention. See FAQ 5.8. If I
have to gather fragments of code from your post and then massage it into
compilation by adding some other parts like the shit you decided wasn't
important, etc., it's *too much work* for *no pay*. Keep that in mind when
asking for help, please.

Sorry, that I formulated my question to generally. It was not my purpose
that you should try to get the same error message from the compiler as I
did. I only wanted to know how to make the correct friend declaration. As I
understand it, a friend declaration does not depend on the exact
implementation of the class and the operator. If you don't now how to make
such a friend declaration, I didn't want you to experiment with the compiler
until the error disappears. I know that it is too much work.
 
V

Victor Bazarov

"Victor Bazarov" wrote in message news:[email protected]...
I have a template class:

[..blah..]
Any suggestion?

Here is a suggestion: post the *simplest possible* code in such a way
that we can copy it from your message and paste it into our text
editor and hit the "build" button. The code you post should produce
the exact message you quote when compiled with the exact compiler you
mention. See FAQ 5.8. If I have to gather fragments of code from your
post and then massage it into compilation by adding some other parts
like the shit you decided wasn't important, etc., it's *too much work*
for *no pay*. Keep that in mind when asking for help, please.

Sorry, that I formulated my question to generally. It was not my purpose
that you should try to get the same error message from the compiler as I
did. I only wanted to know how to make the correct friend declaration.
As I understand it, a friend declaration does not depend on the exact
implementation of the class and the operator. If you don't now how to
make such a friend declaration, I didn't want you to experiment with the
compiler until the error disappears. I know that it is too much work.

<shrug> Whatever. Let's hope you figure it out by yourself (better) or
somebody smarter and/or not too busy will do your figuring out for you.
Good luck!

V
 
L

Luca Risolia

When I use g++ under Linux
(g++ (SUSE Linux) 4.3.4 [gcc-4_3-branch revision 152973]),
I see a lot of messages like (One long line wrapped by me.):

TestRational.cpp:(.text+0xa93): undefined reference to
`std::basic_ostream<char, std::char_traits<char> >&
Rational::eek:perator<<
<std::basic_ostream<char, std::char_traits<char> > >
(std::basic_ostream<char, std::char_traits<char> >&,
Rational::Rational_t<int> const&)'

It seems that the compiler has no problem with the code, but the linker
has.
Usually this indicates that the friend declaration is interpreted as a
forward declaration, for which no implementation is found. So apparently
something is wrong with the friend declaration. But I don't see how I
can make it work.
Any suggestion?

It seems your ostream_t is just a std::basic_ostream<>, so use the
latter directly if you can:

template <typename Base_t>
class Rational_t {
template<class Ch, class Tr, class Base_t_>
friend std::basic_ostream<Ch, Tr>&
operator<<(std::basic_ostream<Ch, Tr>&, const Rational_t<Base_t_>&);
};

template<class Ch, class Tr, class Base_t>
std::basic_ostream<Ch, Tr>& operator<<(std::basic_ostream<Ch, Tr>& out,
const Rational_t<Base_t>& rat) {
}
 
N

Nobody

template <typename Base_t> class Rational_t {

template <typename ostream_t>
friend ostream_t& operator<<
(ostream_t& out, const Rational_t & Rat);
When I use g++ under Linux
(g++ (SUSE Linux) 4.3.4 [gcc-4_3-branch revision 152973]),
I see a lot of messages like (One long line wrapped by me.):

TestRational.cpp:(.text+0xa93): undefined reference to
`std::basic_ostream<char, std::char_traits<char> >&
Rational::eek:perator<<
<std::basic_ostream<char, std::char_traits<char> > >
(std::basic_ostream<char, std::char_traits<char> >&,
Rational::Rational_t<int> const&)'

It seems that the compiler has no problem with the code, but the linker has.

AFAICT, the friend declaration causes the compiler to assume the existence
of multiple operator<< function templates, one for each instance of the
rational template, each with the ostream type as the sole template
parameter, i.e.

template <typename ostream_t>
ostream_t& operator<<(ostream_t& out, const Rational_t<int> & Rat);

template <typename ostream_t>
ostream_t& operator<<(ostream_t& out, const Rational_t<long> & Rat);

....

rather than a single function template with both the ostream and rational
types as parameters, i.e.

template <typename ostream_t, typename base_t>
ostream_t& operator<<(ostream_t& out, const Rational_t<base_t> & Rat);

Because the template parameters are part of the mangled symbol name,
calling the former while only defining the latter will result in an
undefined symbol error.

This works:

template <typename ostream_t, typename base_t>
friend ostream_t& operator<<(ostream_t& out, const Rational_t<base_t>& Rat);

It's more general than your attempt, i.e. all such operator<< functions
are friends of all Rational_t instances rather than just the instance used
in the second argument. However, I don't know whether it's possible to
friend a partial function specialisation (I know that you can't *define* a
partial function specialisation).
 
G

Gerhard Fiedler

Fred said:
"Victor Bazarov" wrote in message news:[email protected]...

Sorry, that I formulated my question to generally. It was not my
purpose that you should try to get the same error message from the
compiler as I did. I only wanted to know how to make the correct
friend declaration. As I understand it, a friend declaration does not
depend on the exact implementation of the class and the operator. If
you don't now how to make such a friend declaration, I didn't want
you to experiment with the compiler until the error disappears. I
know that it is too much work.

Consider also that reducing the problem to the simplest possible code
that shows the problem behavior is most of the time an interesting
learning exercise. It challenges your understanding, makes you think
differently than by looking at the problem in the context of your
specific application, and often you'll find the answer yourself while
doing this.

If you still don't find an answer, you can easily do what Victor asked
you to do, because you have this complete (and often rather simple) code
example right in front of you -- and most of the time with some
additional (and specific) information about things you tried that didn't
work, specific (and reproducible) error messages etc.

Gerhard
 
F

Fred Zwarts \(KVI\)

"Nobody" wrote in message
template <typename Base_t> class Rational_t {

template <typename ostream_t>
friend ostream_t& operator<<
(ostream_t& out, const Rational_t & Rat);
When I use g++ under Linux
(g++ (SUSE Linux) 4.3.4 [gcc-4_3-branch revision 152973]),
I see a lot of messages like (One long line wrapped by me.):

TestRational.cpp:(.text+0xa93): undefined reference to
`std::basic_ostream<char, std::char_traits<char> >&
Rational::eek:perator<<
<std::basic_ostream<char, std::char_traits<char> > >
(std::basic_ostream<char, std::char_traits<char> >&,
Rational::Rational_t<int> const&)'

It seems that the compiler has no problem with the code, but the linker
has.

AFAICT, the friend declaration causes the compiler to assume the existence
of multiple operator<< function templates, one for each instance of the
rational template, each with the ostream type as the sole template
parameter, i.e.

template <typename ostream_t>
ostream_t& operator<<(ostream_t& out, const Rational_t<int> & Rat);

template <typename ostream_t>
ostream_t& operator<<(ostream_t& out, const Rational_t<long> & Rat);

...

I had tried also the following friend declaration:

template <typename ostream_t>
ostream_t& operator<<(ostream_t& out, const Rational_t<Base_t> & Rat);

but it resulted in the same erro message.
rather than a single function template with both the ostream and rational
types as parameters, i.e.

template <typename ostream_t, typename base_t>
ostream_t& operator<<(ostream_t& out, const Rational_t<base_t> & Rat);

Because the template parameters are part of the mangled symbol name,
calling the former while only defining the latter will result in an
undefined symbol error.

This works:

template <typename ostream_t, typename base_t>
friend ostream_t& operator<<(ostream_t& out, const Rational_t<base_t>&
Rat);

It's more general than your attempt, i.e. all such operator<< functions
are friends of all Rational_t instances rather than just the instance used
in the second argument. However, I don't know whether it's possible to
friend a partial function specialisation (I know that you can't *define* a
partial function specialisation).

Thanks. That is the solution. (Although as you noticed a bit too general,
but in this case it does not matter.)

Probably this all means that it is not possible to friend a partial function
specialisation. I don't have the C++ standard here, so I can't check.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,982
Messages
2,570,190
Members
46,736
Latest member
zacharyharris

Latest Threads

Top