I’ve started playing around with extern templates a bit and I’ve stumbled on an issue which I can’t find any relevant information on. Say I have a class template with a non-template friend function (defined within the class template declaration). I declare some extern template instantiations for the class, but how do I declare the friend function as extern too?
Here is some example code:
// --- test.h ---
template <typename T>
class Foo {
private:
T value;
public:
friend void some_friend_function(Foo<T>& obj) {
obj.value -= T(42);
};
void some_member_function(T rhs) { value += rhs; };
};
extern template class Foo<int>;
//extern void some_friend_function(Foo<int>&); // I tried this also...
// --- test.cpp ---
#include "test.h"
template class Foo<int>;
//void some_friend_function(Foo<int>&); // ... with this.
When I compile the above (with or without the commented lines), I only get the following exported symbol:
0000000000000000 W _ZN3FooIiE20some_member_functionEi
So, the non-template friend functions definitely don’t get instantiated (and extern‘d) along with the class template’s explicit instantiation. Is this normal? At least, that’s what GCC produces (tested on 4.6.3 and 4.7.2).
Is there any way that I can get the friend function to be marked extern? I know that this isn’t a huge problem, since I can happily live with the friend functions being instantiated as needed (i.e., non-extern), but I’m curious to know if there is a way to do this, if not, was it an oversight or a deliberate thing?
EDIT: The obvious workarounds
My question is specifically about non-template friend functions, not about finding a workaround to avoid the issue, which is trivial. The first obvious workaround is this:
template <typename T>
class Foo {
private:
T value;
public:
template <typename U>
friend void some_friend_function(Foo<U>& obj) {
obj.value -= T(42);
};
};
extern template class Foo<int>;
extern template void some_friend_function(Foo<int>&);
// --- in cpp file: ---
template class Foo<int>;
template void some_friend_function(Foo<int>&);
And another, which matches more closely but is more troublesome, is this:
template <typename T> class Foo; // forward-declare.
template <typename T>
void some_friend_function(Foo<T>&); // declaration.
template <typename T>
class Foo {
private:
T value;
public:
friend void some_friend_function<>(Foo<T>& obj); // befriend the T-specialization.
};
template <typename T>
void some_friend_function(Foo<T>& obj) { // definition.
obj.value -= T(42);
};
extern template class Foo<int>;
extern template void some_friend_function(Foo<int>&);
// --- in cpp file: ---
template class Foo<int>;
template void some_friend_function(Foo<int>&);
The effect of an “extern template class” is to declare an explicit instantiation to be available. The effect of explicit instantiation declarations do not apply to inline functions or template specializations (14.7.2 [temp.explicit] paragraph 10):
Since a
friendfunction definition within a class definition is necessarily aninlinefunction, it will stay aninlinefunction independent of the explicit instantiation declaration of the template (and, as you correctly noted, it isn’t a template and doesn’t follow template instantiation rules anyway).