friends of multiple templates

K

Klaas Vantournhout

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
 
V

Victor Bazarov

Klaas said:
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
 
S

Sylvester Hesp

Victor Bazarov said:
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
 
K

Kai-Uwe Bux

Sylvester said:
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
 
S

Sylvester Hesp

Sylvester Hesp said:
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
 
K

Klaas Vantournhout

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
 
K

Kai-Uwe Bux

Klaas Vantournhout wrote [top-posting corrected]
Kai-Uwe Bux said:
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
 
S

Sylvester Hesp

Klaas Vantournhout said:
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
 

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,755
Messages
2,569,537
Members
45,020
Latest member
GenesisGai

Latest Threads

Top