How to declare a friend function of a template class?

R

Rui Maciel

I have a class which has a set of friend functions. Now I wish to convert that class to a template
class but I'm having trouble declaring the friend functions of that template class. Can anyone
help? Any tip is more than welcomed.


Thanks in advance,
Rui Maciel
 
V

Victor Bazarov

I have a class which has a set of friend functions. Now I wish to convert that class to a template
class but I'm having trouble declaring the friend functions of that template class. Can anyone
help? Any tip is more than welcomed.

class Boo { // has all instance of 'Foo' as friends
template<class T> friend class Foo;
};

If that doesn't fit your needs, perhaps you need to be more specific
(and post some code this time).

V
 
F

Francesco S. Carta

I have a class which has a set of friend functions. Now I wish to convert that class to a template
class but I'm having trouble declaring the friend functions of that template class. Can anyone
help? Any tip is more than welcomed.

It's a bit tricky, but possible:

//-------
template<class T> class foo {
public:
foo(T t): t(t) {}
protected:
T t;
template<class U> friend void inspect_foo(foo<U> f);
};

template<class T> void inspect_foo(foo<T> f) {
cout << f.t << endl;
}
//-------
 
F

Francesco S. Carta

It's a bit tricky, but possible:

//-------
template<class T> class foo {
public:
foo(T t): t(t) {}
protected:
T t;
template<class U> friend void inspect_foo(foo<U> f);
};

template<class T> void inspect_foo(foo<T> f) {
cout << f.t << endl;
}
//-------

Well, no, the word I meant to use wasn't exactly "tricky" but more on
the side of "verbose / tedious" - if you have lots of overloads it's way
easier to put them in a class and make that a friend, just like
suggested by Victor.

I wonder what's the rationale of not allowing to simply say:

friend function_name;

and set all overloads of "function_name" as friends, regardless of their
arguments or return types - I suppose the mechanism is not as simple as
I'd expect it to be, with these words of mine.
 
P

Paul Bibbings

Francesco S. Carta said:
It's a bit tricky, but possible:

//-------
template<class T> class foo {
public:
foo(T t): t(t) {}
protected:
T t;
template<class U> friend void inspect_foo(foo<U> f);
};

template<class T> void inspect_foo(foo<T> f) {
cout << f.t << endl;
}
//-------

This is not an incorrect example, I do not think. However, note that
parameterization of inspect_foo as a friend of class foo makes *all*
specializations of inspect_foo a friend of any particular specialization
of class foo, which seems a little arbitrary. If, on the other hand, we
look at a similar example in which only inspect_foo<aType>(foo<aType>)
is a friend of foo<aType>, then the requirements of declaring a function
template a friend of a parameterized type begin to look a little less
straight forward, as I believe the following would be required:

#include <iostream>

using namespace std;

template<class T>
class foo; // forward declaration of foo

template<class T>
void inspect_foo(foo<T>); // foward declaration of friend

template<class T>
class foo { // definition of foo
public:
foo(T t): t(t) { }
protected:
T t;
friend void inspect_foo<>(foo<T> f);
};

template<class T>
void inspect_foo(foo<T> f) { // definition of friend function
cout << f.t << endl;
}

int main()
{
inspect_foo(foo<int>(1));
}

Regards

Paul Bibbings
 
F

Francesco S. Carta

This is not an incorrect example, I do not think. However, note that
parameterization of inspect_foo as a friend of class foo makes *all*
specializations of inspect_foo a friend of any particular specialization
of class foo, which seems a little arbitrary.

I don't think that would be a problem: after all, there is no way to
pass a "wrong" foo to inspect_foo.
If, on the other hand, we
look at a similar example in which only inspect_foo<aType>(foo<aType>)
is a friend of foo<aType>, then the requirements of declaring a function
template a friend of a parameterized type begin to look a little less
straight forward, as I believe the following would be required:

#include<iostream>

using namespace std;

template<class T>
class foo; // forward declaration of foo

template<class T>
void inspect_foo(foo<T>); // foward declaration of friend

template<class T>
class foo { // definition of foo
public:
foo(T t): t(t) { }
protected:
T t;
friend void inspect_foo<>(foo<T> f);
};

template<class T>
void inspect_foo(foo<T> f) { // definition of friend function
cout<< f.t<< endl;
}

int main()
{
inspect_foo(foo<int>(1));
}

As a further proof that C++ can really become tedious and verbose, at times.

Luckily, we can declare friend classes and fly straight over all this
awful machinery ;-)
 
R

Rui Maciel

Victor said:
class Boo { // has all instance of 'Foo' as friends
template<class T> friend class Foo;
};

If that doesn't fit your needs, perhaps you need to be more specific
(and post some code this time).

Thanks for the help, Victor. Nonetheless, instead of a friend class I was looking for a way to
declare friend functions of a template class, like Francesco did.


Thanks anyway,
Rui Maciel
 
R

Rui Maciel

Francesco said:
It's a bit tricky, but possible:

//-------
template<class T> class foo {
public:
foo(T t): t(t) {}
protected:
T t;
template<class U> friend void inspect_foo(foo<U> f);
};

template<class T> void inspect_foo(foo<T> f) {
cout << f.t << endl;
}
//-------

I've followed your suggestion and it appears that it works. I thought that the declaration of a
template friend function would be something like:

<code>

template<class T> class foo {
public:
foo(T t): t(t) {}
protected:
T t;
template<class T> friend void inspect_foo(foo<T> f);
};

template<class T> void inspect_foo(foo<T> f) {
cout << f.t << endl;
}

</code>

Is there a reason for that not to work?


Rui Maciel
 
F

Francesco S. Carta

I've followed your suggestion and it appears that it works. I thought that the declaration of a
template friend function would be something like:

<code>

template<class T> class foo {
public:
foo(T t): t(t) {}
protected:
T t;
template<class T> friend void inspect_foo(foo<T> f);
};

template<class T> void inspect_foo(foo<T> f) {
cout<< f.t<< endl;
}

</code>

Is there a reason for that not to work?

The above is reported as "shadowing" by my compiler, and, at the same
time, it tells that it happens to be an error.

I don't really know the rationale behind making such "shadowing" an
error instead of (just) a warning.

By the way, just for fiddling, I've made this test which sort of take
advantage of NOT shadowing T (well, OK, I am not allowed to shadow it in
any case, so no big "taking"):

//-------
#include <iostream>

using namespace std;

template<class T> class foo {
public:
foo(T t): t(t) {}
T get_t() {
return t;
}
private:
T t;
template<class U> friend void inspect(foo<U> f, T t);
};

template<class W> void inspect(foo<W> f, int i) {
// accesses the private interface
cout << f.t << endl;
cout << i << endl;
}

template<class W> void inspect(foo<W> f, double d) {
// accesses the private interface
cout << f.t << endl;
cout << d << endl;
}

template<class W> void inspect(foo<W> f, const char* cstr) {
// accesses only the public interface
cout << f.get_t() << endl;
cout << cstr << endl;
}

int main() {
foo<int> i(42);
foo<double> d(4.2);

// (int, int) compiles as a friend
inspect(i, 78);

// (double, double) compiles as a friend
inspect(d, 7.8);

// (not an int, int) error: not a friend!
//inspect(d, 78);

// (not a double, double) error: not a friend!
//inspect(i, 7.8);

// (whatever, const char*) compiles - not a friend, no need to
inspect(i, "int");
inspect(d, "double");

return 0;
}
//-------


In the above code only the "inspect" instantiations whose second
parameter's type matches the (inspect's) template type are allowed to
access the "innards" of "foo" (whatever its type), all the others can
only access its public interface.

I'm not able to see any immediate use of this - although I think someone
must have used something like this, somewhere.

The point could be: to do the above I've "had" to use the name "T" in
the declaration of "inspect", within of "foo".

Had I used "T" as "inspect" template parameter (shadowing "foo"'s
template parameter) that trick would have been impossible.

And with "trick" I mean "declaring, with a single line, a friend
function that is a /true friend/ only when its parameters equate each other"

Still, that's not enough of a reason to forbid shadowing.

I'm not so sure I could be able to understand all that I wrote here above.

That could be my English' fault. That could be the late hour's fault.
That could well be the templates' fault - or my understanding of them,
more likely ;-)

Templates: lovely, tricky bastards. Good night.
 
V

Vladimir Jovic

Rui said:
I've followed your suggestion and it appears that it works. I thought that the declaration of a
template friend function would be something like:

<code>

template<class T> class foo {
public:
foo(T t): t(t) {}
protected:
T t;
template<class T> friend void inspect_foo(foo<T> f);
};

template<class T> void inspect_foo(foo<T> f) {
cout << f.t << endl;
}

</code>

Is there a reason for that not to work?

1) you are missing main() and includes
2) When you fix 1, then you should get a compiler complaining about
shadowing a template parameter. The problem is, you are declaring two
class types with the same name T, and that is not allowed. How would the
compiler differentiate two?

Maybe you wanted something like this :


#include <iostream>
using namespace std;
template<class T> class foo {
public:
foo(T t): t(t) {}
protected:
T t;
inline friend void inspect_foo( const foo< T > &f )
{
cout << f.t << endl;
}
};

int main()
{
foo< int > a(5);
inspect_foo(a);
}

But see what Paul Bibbings wrote in other tread.
 
B

Bo Persson

Francesco said:
Well, no, the word I meant to use wasn't exactly "tricky" but more
on the side of "verbose / tedious" - if you have lots of overloads
it's way easier to put them in a class and make that a friend, just
like suggested by Victor.

I wonder what's the rationale of not allowing to simply say:

friend function_name;

and set all overloads of "function_name" as friends, regardless of
their arguments or return types - I suppose the mechanism is not as
simple as I'd expect it to be, with these words of mine.

That would let anyone write additional unrelated overloads, and get
access to the private class members. Could as well make them public.


Bo Persson
 
F

Francesco S. Carta

That would let anyone write additional unrelated overloads, and get
access to the private class members. Could as well make them public.

Yes, that's a good reason - a good reason to avoid using it, but maybe
not a good reason to completely forbid it... although I must admit that
having such a feature could lead lazy programmers to abuse it instead of
declaring specifically targeted friend functions... yes, after all, I
think it's better not to have it.

Thanks for pointing that out, I didn't think about it even though that
rationale looks pretty obvious now.
 

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

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top