Search code examples
c++templatesgccclang++type-deduction

templated conversion operator type deduction differs in clang and gcc


I've been fighting differences between compilers with respect to templated conversion operator type deduction for the past few days, and I've finally boiled down at least one of the differences to a stack-overflow-sized example:

void foo(int i);

struct any_const_reference {
  template <typename T>
  operator const T&();
};

template <typename T, typename Enable=void>
struct detect_call : std::false_type { };

template <typename T>
struct detect_call<T,
  decltype( declval<T>()( any_const_reference() ) )
> : std::true_type { };

Demo

The expression detect_call<decltype(foo)>::value give true in clang and false in gcc (using any modern version of either compiler that I can get my hands on, the latest being gcc 5.2 and clang 3.8). Questions:

  • Which is correct? That is, which conforms to the C++ standard?
  • What is causing this difference?
  • In general, what are the type deduction rules being applied to T in the conversion operator (or in any conversion operator of this form, e.g., template <typename T> operator T();), and where are they given in the C++ standard?

Note: this seems to be similar to this question: Template argument type deduction by conversion operator (indeed, I've run into that exact difference as well), but I don't quite see the mapping between that answer and this case. If this is just another instance of this issue, then this question boils down to how to map that issue into this use case.


Solution

  • This can be simplified to just actually calling foo():

    void foo(int ) { }
    
    struct any_const_reference {
      template <typename T>
      operator const T&();
    };
    
    int main() {
        foo(any_const_reference{}); // ok on clang
                                    // error on gcc
    }
    

    This is a gcc bug (#63217 per TC's comment above). Per [temp.deduct.conv], this should deduce T as int since both the reference and qualifiers should be removed (in this case P is const T& and A is int).