The following example compiles fine using gcc and clang, but fails to compile in MSVC. I would like to know if I have unwittingly stumbled into non-standard territory? If not, which compiler is correct? And is there maybe a workaround? Minimal example (https://godbolt.org/z/PG35hPGMW):
#include <iostream>
#include <type_traits>
template <class T>
struct Base {
Base() = default;
Base(T) {}
static constexpr bool isBase = true;
};
template <class U>
constexpr std::enable_if_t<U::isBase, bool> EnableComparisonWithValue(U const *) {
return false;
}
template <class>
constexpr bool EnableComparisonWithValue(...) {
return true;
}
template <class T, class U>
bool operator==(Base<T> const &, Base<U> const &) {
std::cout << "operator==(Base, Base)" << std::endl;
return true;
}
template <class T, class U,
std::enable_if_t<EnableComparisonWithValue<U>(nullptr), int> = 0>
bool operator==(Base<T> const &, U const &) {
std::cout << "operator==(Base, U)" << std::endl;
return true;
}
template <class U, class T,
std::enable_if_t<EnableComparisonWithValue<U>(nullptr), int> = 0>
bool operator==(U const &, Base<T> const &) {
std::cout << "operator==(U, Base)" << std::endl;
return true;
}
int main() {
Base<int> b1, b2;
b1 == 42; // gcc and clang compile, msvc does not
}
MSVC throws a compilation error C2676: binary '==': 'Base<int>' does not define this operator or a conversion to a type acceptable to the predefined operator.
clang and gcc call operator==(Base, U) as expected.
Interestingly, the result is the same if I remove all the members in Base and just define it as template <class T> struct Base{};.
Background: I have another class template <class T> Derived : Base<T> which does not contain additional data. I would like to reuse all the operator== without having to redefine them again for Derived. Without the SFINEA stuff, comparing a Derived<int> with an int results in an ambiguous operator call, because the bottom two operator== definitions deduce U as Derived<int> (AFAIK correctly). So my idea was do disable them to force the compiler to use operator==(Base<T> const &, Base<U> const &). But then I came upon the above problem.
Also, is there maybe a workaround apart from defining the operators for all combinations of Base and Derived?