clang和gcc中的模板转换运算符类型推导不同

templated conversion operator type deduction differs in clang and gcc

本文关键字:类型 运算符 转换 gcc clang      更新时间:2023-10-16

在过去的几天里,我一直在与编译器之间在模板化转换运算符类型推导方面的差异作斗争,我终于将其中至少一个差异归结为堆栈溢出大小的示例:

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 { };

演示

表达式detect_call<decltype(foo)>::value在clang中给出true,在gcc中给出false(使用我能找到的任何现代版本的编译器,最新的是gcc 5.2和clang 3.8)。问题:

  • 哪个是正确的?也就是说,哪个符合C++标准
  • 造成这种差异的原因是什么
  • 一般来说,在转换运算符(或这种形式的任何转换运算符,例如template <typename T> operator T();)中,应用于T的类型推导规则是什么?它们在C++标准中的何处给出

注意:这似乎与这个问题类似:转换运算符的模板参数类型推导(事实上,我也遇到了确切的区别),但我不太清楚这个答案和这个例子之间的映射。如果这只是这个问题的另一个实例,那么这个问题可以归结为如何将这个问题映射到这个用例中。

这可以简化为实际调用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
}

这是一个gcc错误(根据上面TC的评论,#63217)。根据[temp.dexer.conv],这应该将T推断为int,因为引用和限定符都应该被移除(在这种情况下,Pconst T&Aint)。