fghj
fghj

Reputation: 9404

c++ friend function of template with two parameters

Let I have such class Foo and its friend function ::f:

template <typename T>
class Foo {
public:
    template <typename U>
    friend inline void f(Foo<T>, Foo<U>) {}     
};

Then I call it like this f(Foo<double>(), Foo<int>()), friend of who will be ::f?

Only Foo<double> or it will be friend of Foo<int> also, I mean ::f become friend for Foo<T> for any T at the time of declaration, or at the time of usage compiler generate ::f for T=double and find out that it is friend of Foo<double> and not friend of Foo<int>?

And if I declare ::f like this:

template <typename T>
class Foo {
public:
    template <typename U1, typename U2>
    friend inline void f(Foo<U1>, Foo<U2>) {}
};

Again friend of who become ::f after call f(Foo<double>(), Foo<int>());

Note: the second variant compiled only by clang, not gcc.

Note it would be great to see answers with reference to standard.

Upvotes: 2

Views: 351

Answers (1)

Igor Tandetnik
Igor Tandetnik

Reputation: 52611

You don't have a single function template - you have an unbounded family of them. With each instantiation Foo<X>, a new function template is defined,

template <typename U>
void f(Foo<X>, Foo<U>) {}

All instantiations of this template are friends of Foo<X> (but not of any Foo<U> for U != X).

Thus, when you write f(Foo<double>(), Foo<int>()), Foo<double> and Foo<int> are instantiated, and with them, two overloads of f are defined:

template <typename U>
void f(Foo<double>, Foo<U>) {}

template <typename U>
void f(Foo<int>, Foo<U>) {}

Overload resolution selects the first one for the call (the second is not viable), which is instantiated for U==int. This instantiation, f<int>(Foo<double>, Foo<int>), is a friend of Foo<double> but not Foo<int>.


In your second example, I believe gcc is right in rejecting the code, and clang is wrong in accepting it (for what it's worth, MSVC agrees with gcc). Every instantiation of Foo<X> introduces a definition of the same template into the enclosing scope:

template <typename U1, typename U2>
void f(Foo<U1>, Foo<U2>) {}

When a translation unit performs two instantiaions of Foo, it ends up with two identical definitions of f, hence the error.

Upvotes: 2

Related Questions