Template friend in templat class

Discussion in 'C++' started by Fred Zwarts \(KVI\), Jul 20, 2012.

  1. 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?
     
    Fred Zwarts \(KVI\), Jul 20, 2012
    #1
    1. Advertising

  2. "Fred Zwarts (KVI)" wrote in message news:jubolr$kn1$...
    >
    >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.
     
    Fred Zwarts \(KVI\), Jul 20, 2012
    #2
    1. Advertising

  3. On 7/20/2012 10:06 AM, Fred Zwarts (KVI) wrote:
    > 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
    --
    I do not respond to top-posted replies, please don't ask
     
    Victor Bazarov, Jul 20, 2012
    #3
  4. "Victor Bazarov" wrote in message news:jubpl4$5jk$...
    >
    >On 7/20/2012 10:06 AM, Fred Zwarts (KVI) wrote:
    >> 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.
     
    Fred Zwarts \(KVI\), Jul 20, 2012
    #4
  5. On 7/20/2012 11:22 AM, Fred Zwarts (KVI) wrote:
    > "Victor Bazarov" wrote in message news:jubpl4$5jk$...
    >>
    >> On 7/20/2012 10:06 AM, Fred Zwarts (KVI) wrote:
    >>> 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
    --
    I do not respond to top-posted replies, please don't ask
     
    Victor Bazarov, Jul 20, 2012
    #5
  6. Fred Zwarts \(KVI\)

    Luca Risolia Guest

    On 20/07/2012 16:06, Fred Zwarts (KVI) wrote:
    > 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) {
    }
     
    Luca Risolia, Jul 20, 2012
    #6
  7. Fred Zwarts \(KVI\)

    Nobody Guest

    On Fri, 20 Jul 2012 16:06:51 +0200, Fred Zwarts (KVI) wrote:

    > 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).
     
    Nobody, Jul 21, 2012
    #7
  8. Fred Zwarts (KVI) wrote:

    > "Victor Bazarov" wrote in message news:jubpl4$5jk$...
    >> 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.


    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
     
    Gerhard Fiedler, Jul 21, 2012
    #8
  9. "Nobody" wrote in message
    news:p...
    >
    >On Fri, 20 Jul 2012 16:06:51 +0200, Fred Zwarts (KVI) wrote:
    >
    >> 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.
     
    Fred Zwarts \(KVI\), Jul 23, 2012
    #9
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. =?Utf-8?B?TG9yZW56aW5v?=
    Replies:
    7
    Views:
    2,171
    =?Utf-8?B?RHVzdGluIHZhbiBkZSBTYW5kZSBbTUNBRF0=?=
    Apr 14, 2006
  2. Yueh-Wei Hu
    Replies:
    0
    Views:
    464
    Yueh-Wei Hu
    May 23, 2004
  3. Replies:
    2
    Views:
    495
    John Harrison
    Nov 9, 2005
  4. A L
    Replies:
    1
    Views:
    523
    Alf P. Steinbach /Usenet
    Aug 25, 2010
  5. Peter
    Replies:
    2
    Views:
    293
    Öö Tiib
    Jun 6, 2013
Loading...

Share This Page