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?
There are three possible option for you
std::nullptt_t
for ex
inline bool operator ==(foo<T> lhs, std::nullptr_t rhs) { return true; }
nullptr
, explicitly state the type of nullptr
for ex
assert(p == foo<int>(nullptr));
void *
for ex
inline bool operator ==(foo<T> lhs, void *rhs) {
if (rhs == nullptr)
return true;
else
return false;
}
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:
template <typename T>
struct A {
friend void f(A);
};
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:
inline void f(A<int>) {...}
inline void f(A<float>) {...}
// etc.
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:
template <typename T>
inline bool operator ==(foo<T> lhs, std::nullptr_t rhs)
{
return lhs==foo<T>(rhs);
}
You may want to do it both ways for symmetry:
template <typename T>
inline bool operator ==(std::nullptr_t lhs,foo<T> rhs)
{
return foo<T>(lhs)==rhs;
}
On a separate note, the way you've defined your friend function makes operator==(foo<U>,foo<U>)
a friend of foo<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:
template <typename> struct foo;
template <typename T>
inline bool operator==(foo<T> lhs,foo<T> rhs);
template <typename T>
struct foo {
foo(std::nullptr_t) { }
friend bool operator==<>(foo lhs,foo rhs);
};
template <typename T>
inline bool operator ==(foo<T> lhs,foo<T> rhs)
{
return true;
}
template <typename T>
inline bool operator ==(foo<T> lhs, std::nullptr_t rhs)
{
return lhs==foo<T>(rhs);
}
template <typename T>
inline bool operator ==(std::null_ptr_t lhs,foo<T> rhs)
{
return foo<T>(lhs)==rhs;
}
int main() {
foo<int> p = nullptr;
assert(p == nullptr);
assert(nullptr == p);
assert(p == p);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With