The following code
#include <cassert>
#include <cstddef>
template <typename T>
struct foo {
foo(std::nullptr_t) { }
//friend bool operator ==(foo lhs, foo rhs) { return true; }
template <typename U>
friend bool operator ==(foo<U> lhs, foo<U> rhs);
};
template <typename T>
inline bool operator ==(foo<T> lhs, foo<T> rhs) { return true; }
int main() {
foo<int> p = nullptr;
assert(p == nullptr);
}
fails to compile with the error message
foo.cpp:18:5: error: no match for ‘
operator==‘ in ‘p == nullptr‘
foo.cpp:18:5: note: candidate is:
foo.cpp:14:13: note:template<class T> bool operator==(foo<T>, foo<T>)
foo.cpp:14:13: note: template argument deduction/substitution failed:
foo.cpp:18:5: note: mismatched types ‘foo<T>‘ and ‘std::nullptr_t‘
However, if I use the definition inside the class instead, the code works as expected.
Let me say that I understand the error message: the template argument T cannot be deduced for the type of nullptr (incidentally, decltype(*nullptr) doesn’t compile). Furthermore, this can be fixed here by just defining the function inside the class.
However, for reasons of uniformity (there are other friend functions which I need to define outside) I would like to define this function outside of the class.
Is there a “trick” to make an outside of class definition of the function work?
Abhijit has already given you the basic solution, but I thought I would expound upon it a bit since this is an interesting problem.
If you declare a friend function inside a template class, like this:
Then what you are saying is that any non-template function called f that takes A as a parameter will be a friend of A. So you would need to define these functions separately:
Although defining it inside the class is a shortcut.
In this case, there is no way to make a template that defines the friend f(A) for every T, because you’ve already stated that it is the non-template function that is the friend. It is the very fact that it is a non-template function that makes it usable in your example, because non-template functions allow more conversions than template functions when the compiler looks for matching functions.
There is a fairly general workaround, although it is a bit messy. You can define other template functions that will handle your nullptr, or whatever else you might throw at it, and just defer it to your main function:
You may want to do it both ways for symmetry:
On a separate note, the way you’ve defined your friend function makes
operator==(foo<U>,foo<U>)a friend offoo<T>even when U and T are not the same types. It probably isn’t going to make much difference in practice, but there is a technically better way to do this. It involves forward declaring the template function, and then making the specialization for your template parameter be a friend.Here is a full example: