friends of multiple templates

Discussion in 'C++' started by Klaas Vantournhout, Jan 24, 2007.

  1. Hi all,

    I have a question about friends functions of a template class.

    To make it simple, I would like to do something like this.

    Assume that I have a class foo with template T

    template<T> class foo

    It is easy to define a friend function bar, depending on two foo<T>'s.
    friend foo<T> bar<>(foo<T>,foo<T>);

    But how do you define it with a foo<U> and a foo<T>?
    friend foo<T> bar<>(foo<U>,foo<T>);


    Thanks for the help
    Klaas Vantournhout, Jan 24, 2007
    #1
    1. Advertising

  2. Klaas Vantournhout wrote:
    > Hi all,
    >
    > I have a question about friends functions of a template class.
    >
    > To make it simple, I would like to do something like this.
    >
    > Assume that I have a class foo with template T
    >
    > template<T> class foo
    >
    > It is easy to define a friend function bar, depending on two foo<T>'s.
    > friend foo<T> bar<>(foo<T>,foo<T>);
    >
    > But how do you define it with a foo<U> and a foo<T>?
    > friend foo<T> bar<>(foo<U>,foo<T>);


    template<class T> class foo;

    template<class T, class U>
    foo<T> bar(foo<T>, foo<U>);

    template<class T> class foo
    {
    int a;
    template<class U> friend foo<T> bar(foo<T>, foo<U>);
    };

    template<class T, class U>
    foo<T> bar(foo<T> fT, foo<U> fU)
    {
    fT.a + fU.a;
    return fT;
    }

    int main() {
    foo<int> fint;
    foo<double> fdouble;
    bar(fint, fdouble);
    }


    V
    --
    Please remove capital 'A's when replying by e-mail
    I do not respond to top-posted replies, please don't ask
    Victor Bazarov, Jan 24, 2007
    #2
    1. Advertising

  3. "Victor Bazarov" <> wrote in message
    news:ep7rlc$7up$...
    > template<class T> class foo;
    >
    > template<class T, class U>
    > foo<T> bar(foo<T>, foo<U>);
    >
    > template<class T> class foo
    > {
    > int a;
    > template<class U> friend foo<T> bar(foo<T>, foo<U>);
    > };
    >
    > template<class T, class U>
    > foo<T> bar(foo<T> fT, foo<U> fU)
    > {
    > fT.a + fU.a;
    > return fT;
    > }
    >
    > int main() {
    > foo<int> fint;
    > foo<double> fdouble;
    > bar(fint, fdouble);
    > }
    >


    I'm a bit curious why this works (although both comeau and gcc seem to allow
    it, however gcc complains with a link error (undefined reference to
    `foo<int> bar<double>(foo<int>, foo<double>)').

    foo<T> declares bar<T,U> as a friend, but not bar<U,T>. Therefore,
    foo<double>::a shouldn't be accessible in the function bar<int, double> (as
    that is not a bar<T,U> but a bar<U,T> to foo<T> with T=double and U=int).
    Sadly enough, the standard isn't very clear about these specific cases (or
    perhaps I'm misinterpreting something in 14.5.3).

    - Sylvester
    Sylvester Hesp, Jan 26, 2007
    #3
  4. Klaas Vantournhout

    Kai-Uwe Bux Guest

    Sylvester Hesp wrote:

    >
    > "Victor Bazarov" <> wrote in message
    > news:ep7rlc$7up$...
    >> template<class T> class foo;
    >>
    >> template<class T, class U>
    >> foo<T> bar(foo<T>, foo<U>);
    >>
    >> template<class T> class foo
    >> {
    >> int a;
    >> template<class U> friend foo<T> bar(foo<T>, foo<U>);
    >> };
    >>
    >> template<class T, class U>
    >> foo<T> bar(foo<T> fT, foo<U> fU)
    >> {
    >> fT.a + fU.a;
    >> return fT;
    >> }
    >>
    >> int main() {
    >> foo<int> fint;
    >> foo<double> fdouble;
    >> bar(fint, fdouble);
    >> }
    >>

    >
    > I'm a bit curious why this works (although both comeau and gcc seem to
    > allow it, however gcc complains with a link error (undefined reference to
    > `foo<int> bar<double>(foo<int>, foo<double>)').
    >
    > foo<T> declares bar<T,U> as a friend, but not bar<U,T>. Therefore,
    > foo<double>::a shouldn't be accessible in the function bar<int, double>
    > (as that is not a bar<T,U> but a bar<U,T> to foo<T> with T=double and
    > U=int). Sadly enough, the standard isn't very clear about these specific
    > cases (or perhaps I'm misinterpreting something in 14.5.3).



    It doesn't work. As a matter of fact, the code above introduces two versions
    of bar. The friend version is what is called in main. Since the body of
    that function never tries to access private members of foo<U>, the compiler
    has no reason to complain. On the other hand, since that function is never
    defined, you get a linker error. Let's define it to test this theory:

    #include <iostream>

    template<class T> class foo;

    template<class T, class U>
    foo<T> bar(foo<T>, foo<U>);

    template<class T> class foo
    {
    int a;
    template<class U> friend foo<T> bar(foo<T> a, foo<U> b) {
    std::cout << "template friend called\n";
    return ( a );
    }
    };

    template<class T, class U>
    foo<T> bar(foo<T> fT, foo<U> fU)
    {
    std::cout << "freestanding template called\n";
    fT.a + fU.a;
    return fT;
    }

    int main() {
    foo<int> fint;
    foo<double> fdouble;
    bar(fint, fdouble);
    }


    This prints:

    template friend called


    However, as soon as the friend tries to access private members of foo<U>,
    the code does not compile:

    #include <iostream>

    template<class T> class foo;

    template<class T, class U>
    foo<T> bar(foo<T>, foo<U>);

    template<class T> class foo
    {
    int a;
    template<class U> friend foo<T> bar(foo<T> a, foo<U> b) {
    std::cout << "template friend called\n";
    b.a;
    return ( a );
    }
    };

    template<class T, class U>
    foo<T> bar(foo<T> fT, foo<U> fU)
    {
    std::cout << "freestanding template called\n";
    fT.a + fU.a;
    return fT;
    }

    int main() {
    foo<int> fint;
    foo<double> fdouble;
    bar(fint, fdouble);
    }



    As for the original question: I have no idea how to do it without making the
    function befriend of unrelated classes, too. But, what about:

    #include <iostream>

    template<class T> class foo;

    template<class T, class U>
    foo<T> bar(foo<T> fT, foo<U> fU);

    template<class T> class foo
    {
    int a;

    template< class A, class B >
    friend
    foo<A> bar ( foo<A>, foo<B> );

    };

    template<class T, class U>
    foo<T> bar(foo<T> fT, foo<U> fU)
    {
    std::cout << "freestanding template called\n";
    fT.a + fU.a;
    return fT;
    }

    int main() {
    foo<int> fint;
    foo<double> fdouble;
    bar(fint, fdouble);
    }


    Best

    Kai-Uwe Bux
    Kai-Uwe Bux, Jan 26, 2007
    #4
  5. "Sylvester Hesp" <> wrote in message
    news:45ba042d$0$330$4all.nl...
    >
    > "Victor Bazarov" <> wrote in message
    > news:ep7rlc$7up$...
    >> template<class T> class foo;
    >>
    >> template<class T, class U>
    >> foo<T> bar(foo<T>, foo<U>);
    >>
    >> template<class T> class foo
    >> {
    >> int a;
    >> template<class U> friend foo<T> bar(foo<T>, foo<U>);
    >> };
    >>
    >> template<class T, class U>
    >> foo<T> bar(foo<T> fT, foo<U> fU)
    >> {
    >> fT.a + fU.a;
    >> return fT;
    >> }
    >>
    >> int main() {
    >> foo<int> fint;
    >> foo<double> fdouble;
    >> bar(fint, fdouble);
    >> }
    >>

    >
    > I'm a bit curious why this works (although both comeau and gcc seem to
    > allow it, however gcc complains with a link error (undefined reference to
    > `foo<int> bar<double>(foo<int>, foo<double>)').
    >


    Actually, after pondering over it for a while, I realized this doesn't do
    what you want, and gcc's link error is correct. bar<T,U> is never
    instantiated, as the friend declaration in foo<T> declares *another*
    function bar<U> (thus with only 1 template argument), with one of the
    parameters fixed to foo<T>. As an actual definition of bar<U> isn't given,
    this results in link errors. Since bar<T,U> is never instantiated, it
    doesn't produce errors.

    Now, if you change the template friend function declaration to a
    *definition*, the error actually shows up:

    template<class T> class foo
    {
    int a;
    template<class U> friend foo<T> bar(foo<T> fT, foo<U> fU)
    {
    fT.a + fU.a;
    return fT;
    }
    };

    int main()
    {
    foo<int> fint;
    foo<double> fdouble;
    bar(fint, fdouble);
    }

    Here, both comeau, gcc and VC++ complain about fU.a not being accessible.


    This problem don't seem to be solveable at all. When changing foo to

    template<class T> class foo
    {
    int a;
    template<class, class U> friend foo<T> bar(foo<T>, foo<U>);
    template<class U, class> friend foo<U> bar(foo<U>, foo<T>);
    };

    which would, in my opinion, be the correct friend declarations for our
    intents and purposes, both Comeau and GCC complain about both fT.a and fU.a
    not being accessible. (VC++ 8.0 still allows it, but I'm not so surprised by
    that ;))

    - Sylvester
    Sylvester Hesp, Jan 26, 2007
    #5
  6. "Kai-Uwe Bux" <> wrote in message
    news:epd5b8$g5m$...
    > It doesn't work

    Yeah I just realized that, see my other post ;)
    Sylvester Hesp, Jan 26, 2007
    #6
  7. Hi Kai and Sylvester,

    Thanks for the responses. This is indeed what I was looking for. I
    never thought of introducing two different kind of templates for that.

    A nice new trick stored in my memory!

    But now what if the bar function is an operator like operator+, will
    this still work?

    regards

    Klaas

    Kai-Uwe Bux wrote:
    > Sylvester Hesp wrote:
    >
    >> "Victor Bazarov" <> wrote in message
    >> news:ep7rlc$7up$...
    >>> template<class T> class foo;
    >>>
    >>> template<class T, class U>
    >>> foo<T> bar(foo<T>, foo<U>);
    >>>
    >>> template<class T> class foo
    >>> {
    >>> int a;
    >>> template<class U> friend foo<T> bar(foo<T>, foo<U>);
    >>> };
    >>>
    >>> template<class T, class U>
    >>> foo<T> bar(foo<T> fT, foo<U> fU)
    >>> {
    >>> fT.a + fU.a;
    >>> return fT;
    >>> }
    >>>
    >>> int main() {
    >>> foo<int> fint;
    >>> foo<double> fdouble;
    >>> bar(fint, fdouble);
    >>> }
    >>>

    >> I'm a bit curious why this works (although both comeau and gcc seem to
    >> allow it, however gcc complains with a link error (undefined reference to
    >> `foo<int> bar<double>(foo<int>, foo<double>)').
    >>
    >> foo<T> declares bar<T,U> as a friend, but not bar<U,T>. Therefore,
    >> foo<double>::a shouldn't be accessible in the function bar<int, double>
    >> (as that is not a bar<T,U> but a bar<U,T> to foo<T> with T=double and
    >> U=int). Sadly enough, the standard isn't very clear about these specific
    >> cases (or perhaps I'm misinterpreting something in 14.5.3).

    >
    >
    > It doesn't work. As a matter of fact, the code above introduces two versions
    > of bar. The friend version is what is called in main. Since the body of
    > that function never tries to access private members of foo<U>, the compiler
    > has no reason to complain. On the other hand, since that function is never
    > defined, you get a linker error. Let's define it to test this theory:
    >
    > #include <iostream>
    >
    > template<class T> class foo;
    >
    > template<class T, class U>
    > foo<T> bar(foo<T>, foo<U>);
    >
    > template<class T> class foo
    > {
    > int a;
    > template<class U> friend foo<T> bar(foo<T> a, foo<U> b) {
    > std::cout << "template friend called\n";
    > return ( a );
    > }
    > };
    >
    > template<class T, class U>
    > foo<T> bar(foo<T> fT, foo<U> fU)
    > {
    > std::cout << "freestanding template called\n";
    > fT.a + fU.a;
    > return fT;
    > }
    >
    > int main() {
    > foo<int> fint;
    > foo<double> fdouble;
    > bar(fint, fdouble);
    > }
    >
    >
    > This prints:
    >
    > template friend called
    >
    >
    > However, as soon as the friend tries to access private members of foo<U>,
    > the code does not compile:
    >
    > #include <iostream>
    >
    > template<class T> class foo;
    >
    > template<class T, class U>
    > foo<T> bar(foo<T>, foo<U>);
    >
    > template<class T> class foo
    > {
    > int a;
    > template<class U> friend foo<T> bar(foo<T> a, foo<U> b) {
    > std::cout << "template friend called\n";
    > b.a;
    > return ( a );
    > }
    > };
    >
    > template<class T, class U>
    > foo<T> bar(foo<T> fT, foo<U> fU)
    > {
    > std::cout << "freestanding template called\n";
    > fT.a + fU.a;
    > return fT;
    > }
    >
    > int main() {
    > foo<int> fint;
    > foo<double> fdouble;
    > bar(fint, fdouble);
    > }
    >
    >
    >
    > As for the original question: I have no idea how to do it without making the
    > function befriend of unrelated classes, too. But, what about:
    >
    > #include <iostream>
    >
    > template<class T> class foo;
    >
    > template<class T, class U>
    > foo<T> bar(foo<T> fT, foo<U> fU);
    >
    > template<class T> class foo
    > {
    > int a;
    >
    > template< class A, class B >
    > friend
    > foo<A> bar ( foo<A>, foo<B> );
    >
    > };
    >
    > template<class T, class U>
    > foo<T> bar(foo<T> fT, foo<U> fU)
    > {
    > std::cout << "freestanding template called\n";
    > fT.a + fU.a;
    > return fT;
    > }
    >
    > int main() {
    > foo<int> fint;
    > foo<double> fdouble;
    > bar(fint, fdouble);
    > }
    >
    >
    > Best
    >
    > Kai-Uwe Bux
    >
    Klaas Vantournhout, Jan 31, 2007
    #7
  8. Klaas Vantournhout

    Kai-Uwe Bux Guest

    Klaas Vantournhout wrote [top-posting corrected]

    > Kai-Uwe Bux wrote:
    >> Sylvester Hesp wrote:
    >>
    >>> "Victor Bazarov" <> wrote in message
    >>> news:ep7rlc$7up$...
    >>>> template<class T> class foo;
    >>>>
    >>>> template<class T, class U>
    >>>> foo<T> bar(foo<T>, foo<U>);
    >>>>
    >>>> template<class T> class foo
    >>>> {
    >>>> int a;
    >>>> template<class U> friend foo<T> bar(foo<T>, foo<U>);
    >>>> };
    >>>>
    >>>> template<class T, class U>
    >>>> foo<T> bar(foo<T> fT, foo<U> fU)
    >>>> {
    >>>> fT.a + fU.a;
    >>>> return fT;
    >>>> }
    >>>>
    >>>> int main() {
    >>>> foo<int> fint;
    >>>> foo<double> fdouble;
    >>>> bar(fint, fdouble);
    >>>> }
    >>>>
    >>> I'm a bit curious why this works (although both comeau and gcc seem to
    >>> allow it, however gcc complains with a link error (undefined reference
    >>> to `foo<int> bar<double>(foo<int>, foo<double>)').
    >>>
    >>> foo<T> declares bar<T,U> as a friend, but not bar<U,T>. Therefore,
    >>> foo<double>::a shouldn't be accessible in the function bar<int, double>
    >>> (as that is not a bar<T,U> but a bar<U,T> to foo<T> with T=double and
    >>> U=int). Sadly enough, the standard isn't very clear about these specific
    >>> cases (or perhaps I'm misinterpreting something in 14.5.3).

    >>
    >>
    >> It doesn't work.

    [analysis snipped]
    >
    > Hi Kai and Sylvester,
    >
    > Thanks for the responses. This is indeed what I was looking for. I
    > never thought of introducing two different kind of templates for that.
    >
    > A nice new trick stored in my memory!
    >
    > But now what if the bar function is an operator like operator+, will
    > this still work?


    a) Please don't top post. It is frowned upon around these parts.

    b) What about:

    #include <iostream>

    template<class T> class foo;

    template<class T, class U>
    foo<T> operator+ ( foo<T> fT, foo<U> fU );

    template<class T> class foo {
    int a;

    template< class A, class B >
    friend
    foo<A> operator+ ( foo<A>, foo<B> );

    };

    template<class T, class U>
    foo<T> operator+ (foo<T> fT, foo<U> fU) {
    std::cout << "freestanding template called\n";
    fT.a + fU.a;
    return fT;
    }

    int main() {
    foo<int> fint;
    foo<double> fdouble;
    fint + fdouble;
    }


    BTW: operator+ somehow suggests a commutative operaton, and it is usually
    tricky to guess the result type of operator+ just from the lhs. I would
    consider

    template < typename LHS, typename RHS >
    struct result_of_plus {

    typedef some_magic type;

    };

    template < typename LHS, typename RHS >
    typename result_of_plus<LHS,RHS>::type
    operator+ ( foo<LHS> lhs, foo<RHS> rhs ) {
    ...
    }


    Best

    Kai-Uwe Bux
    Kai-Uwe Bux, Jan 31, 2007
    #8
  9. "Klaas Vantournhout" <> wrote in message
    news:epq6co$1v2$...
    > Hi Kai and Sylvester,
    >
    > Thanks for the responses. This is indeed what I was looking for. I never
    > thought of introducing two different kind of templates for that.
    >
    > A nice new trick stored in my memory!
    >
    > But now what if the bar function is an operator like operator+, will this
    > still work?
    >


    Sure, operators behave just like regular functions.

    Btw, please don't top-post. See 5.4 of the C++ faq lite:
    http://www.parashift.com/c -faq-lite/how-to-post.html#faq-5.4

    - Sylvester
    Sylvester Hesp, Jan 31, 2007
    #9
  10. Sylvester Hesp wrote:
    > "Klaas Vantournhout" <> wrote in message
    > news:epq6co$1v2$...
    >> Hi Kai and Sylvester,
    >>
    >> Thanks for the responses. This is indeed what I was looking for. I never
    >> thought of introducing two different kind of templates for that.
    >>
    >> A nice new trick stored in my memory!
    >>
    >> But now what if the bar function is an operator like operator+, will this
    >> still work?
    >>

    >
    > Sure, operators behave just like regular functions.
    >
    > Btw, please don't top-post. See 5.4 of the C++ faq lite:
    > http://www.parashift.com/c -faq-lite/how-to-post.html#faq-5.4
    >
    > - Sylvester
    >
    >


    Okay, forgot this for a while about top-posting.

    Thanks again to both of you for replying.

    regards
    Klaas
    Klaas Vantournhout, Feb 1, 2007
    #10
    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. Kevin Christopher

    templates and friends

    Kevin Christopher, Jul 1, 2003, in forum: C++
    Replies:
    3
    Views:
    345
    tom_usenet
    Jul 2, 2003
  2. JKop
    Replies:
    3
    Views:
    468
  3. Replies:
    2
    Views:
    597
  4. recover
    Replies:
    2
    Views:
    802
    recover
    Jul 25, 2006
  5. Replies:
    0
    Views:
    658
Loading...

Share This Page