Adding operator> for classes that don't have one shows different results on different compilers

Viewed 90

Recently I wrote some experimental code to add operator> for all classes without one. The code is here:

#include <cstdio>
#include <experimental/type_traits>
#include <type_traits>

struct A {
    A(int x) : x(x) {}
    int x;
    bool operator<(const A &rhs) const {
        printf("original lt\n");
        return x < rhs.x;
    }
};

template <class T> using gt_t = decltype(std::declval<T>() > std::declval<T>());

template <class Key>
std::enable_if_t<!std::experimental::is_detected<gt_t, Key>::value, bool>
operator>(const Key &lhs, const Key &rhs) {
    printf("generated gt\n");
    return rhs < lhs;
}

int main() {
    A a(1);
    A b(2);
    printf("%d\n", b > a);
}

I used "-O2 -std=c++14" to compile, gcc 8.1 and later versions compiled successfully, while gcc 7.5 and older versions generated compile errors ( godbolt link ) like

<source>: In substitution of 'template<class T> using gt_t = decltype ((declval<T>() > declval<T>())) [with T = A]':
<source>:17:18:   required by substitution of 'template<class Key> std::enable_if_t<(! typename std::__detector<std::experimental::fundamentals_v2::nonesuch, void, gt_t, Key>::value_t:: value), bool> operator>(const Key&, const Key&) [with Key = A]'
<source>:14:60:   required by substitution of 'template<class T> using gt_t = decltype ((declval<T>() > declval<T>())) [with T = A]'
<source>:17:18:   required by substitution of 'template<class Key> std::enable_if_t<(! typename std::__detector<std::experimental::fundamentals_v2::nonesuch, void, gt_t, Key>::value_t:: value), bool> operator>(const Key&, const Key&) [with Key = A]'
<source>:14:60:   [ skipping 589 instantiation contexts, use -ftemplate-backtrace-limit=0 to disable ]
<source>:17:18:   recursively required by substitution of 'template<class _Default, template<class ...> class _Op, class ... _Args> struct std::__detector<_Default, std::__void_t<_Op<_Args ...> >, _Op, _Args ...> [with _Default = std::experimental::fundamentals_v2::nonesuch; _Op = gt_t; _Args = {A}]'
<source>:17:18:   required by substitution of 'template<class Key> std::enable_if_t<(! typename std::__detector<std::experimental::fundamentals_v2::nonesuch, void, gt_t, Key>::value_t:: value), bool> operator>(const Key&, const Key&) [with Key = A]'
<source>:17:18:   recursively required by substitution of 'template<class _Default, template<class ...> class _Op, class ... _Args> struct std::__detector<_Default, std::__void_t<_Op<_Args ...> >, _Op, _Args ...> [with _Default = std::experimental::fundamentals_v2::nonesuch; _Op = gt_t; _Args = {A}]'

The errors look reasonable to me, because adding operator> for classes that don't have one is somewhat like Russell's paradox. However, compilers like gcc 8.1 and clang 5.0 can actually compile it.

I know there are several methods to solve the original problem (and I already solved it). I just want to know if that code is valid or not, and why different versions of gcc produces different results.

0 Answers
Related