Fun with conversion-operator name lookup

As of this writing (but perhaps not for very much longer!) the four mainstream compilers on Godbolt Compiler Explorer give four different answers for this simple C++ program:

struct A {
    using T = T1;
    using U = U1;
    operator U1 T1::*();
    operator U1 T2::*();
    operator U2 T1::*();
    operator U2 T2::*();
};

inline auto which(U1 T1::*) { return "gcc"; }
inline auto which(U1 T2::*) { return "icc"; }
inline auto which(U2 T1::*) { return "msvc"; }
inline auto which(U2 T2::*) { return "clang"; }

int main() {
    A a;
    using T = T2;
    using U = U2;
    puts(which(a.operator U T::*()));
}

The question is whether U should be looked up in the scope of test or in the scope of A; and the same question for T.

According to the current draft standard, it sounds like the conforming answer is “they should both be looked up in the scope of A”; i.e., GCC’s answer is correct and the others are wrong in three different ways. [basic.lookup.unqual]/5:

An unqualified name that is a component name of a type-specifier or ptr-operator of a conversion-type-id is looked up in the same fashion as the conversion-function-id in which it appears. If that lookup finds nothing, it undergoes unqualified name lookup; in each case, only names that denote types or templates whose specializations are types are considered.

I’m never a fan of lookups that don’t consider certain kinds of names; I’m sure there’s more divergence to be discovered in this area. Anyway, in the type name U T::*, U is the type-specifier and T::* is the ptr-operator, and the whole type is pronounced “pointer to a data member of T, where that data member itself is of type U.” (More concisely: “pointer to data member (of type U) of T,” or “pointer to a U member of T.”)

See also:

Posted 2021-01-13