Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Define friend function outside class definition when the class and function have separate template parameters

The following code compiles fine (just a minimal example, the actual code has more of a reason for this particular layout):

template<int A>
class Foo {
    template<int B>
    friend int bar(Foo<A> a, Foo<B> b) {
        return A * B;
    }
};

int main() {
    return bar(Foo<0>(), Foo<1>());
}

However, I would like to separate the declaration from the definition and put the function definition outside the class definition. I've tried doing it as you would for a member function:

template<int A> template<int B>
int bar(Foo<A> a, Foo<B> b) {
    return A * B;
}

But that fails to compile because it doesn't seem to be valid syntax:

error: too many template-parameter-lists

However, merging the two template parameter lists in this definition (template<int A, int B>) gives a linker error:

undefined reference to `int bar<1>(Foo<0>, Foo<1>)'

That leads me to believe that different template parameter lists cause the compiler/linker to interpret the definition of bar as a different function than the declaration.

So my question is: how can I define a friend function outside of the class definition when the class and function have separate template parameters?

like image 346
cp.fe.cp Avatar asked Oct 16 '22 19:10

cp.fe.cp


1 Answers

I think that there is no way to do this. Well there is but not generically. You can always explicitly write the overloads for each A:

template<int B>
int bar(Foo<0> a, Foo<B> b) {
    return 0 + B;
}

Basically, the reason is that a different bar overload is defined for each instantiation of Foo. For example, for Foo<0> you'll get a

template<int B>
int bar(Foo<0>, Foo<B>);

For each instantiation of Foo, you will get a new bar template. All those bars are independent, because they use a template parameter that exists outside of bar itself (from the outer template that is).

You can't use the double template<> syntax because that would mean you have two things (class, function, ...) that are a template. This is not the case here, as you only have bar as a template. There is nothing else for the first/second template<>.

You can't merge them, because as you saw, you'll get a different function. If you look at the instantiated bar above for Foo<0>, you can see that this is different than:

template<int A, int B>
int bar(Foo<A>, Foo<B>);

The instantiated version will always be a better match because it doesn't have to deduce anything for the first parameter.

like image 140
Rakete1111 Avatar answered Oct 20 '22 20:10

Rakete1111