Class 模板忽略了用户定义的转换运算符(非模板则不然)

User-Defined Conversion Operator Ignored for Class Template (Not So for Non-templates)

这段代码确实可以编译(重要的一点是 F() 只接受 As,并且由于存在从 BA 的隐式转换,我可以轻松地将 B 传递给它。)

struct A {};

struct B {
    constexpr operator A () const {return {};}
};

void F (A a) {}

int main() {
    F(B());
    return 0;
}

但是模板版本无法编译:

template <typename T>
struct A {};

template <typename T>
struct B {
    constexpr operator A<T> () const {return {};}
};

template <typename T>
void F (A<T> a) {}

int main() {
    F(B<int>());
    return 0;
}

在 GCC 上出现以下错误(以及在 MSVC 上的等效错误):

error: no matching function for call to ‘F(B<int>)’

(附加信息表明存在 F(A<>)B 不继承自 A。)

郑重声明,在 A 中实现隐式转换运算符也无济于事。

为什么模板版本编译不通过?我错过了什么吗?或者实际上没有办法用 class 模板来做?!

(注意:我知道我可以在调用站点将 B 显式转换为 A;这不是我喜欢的。)

Template argument deduction 不考虑隐式转换。

Type deduction does not consider implicit conversions (other than type adjustments listed above): that's the job for overload resolution, which happens later.

必须首先推导模板参数T(在重载决议之前),但给定参数A<T>和参数B<int>T不能推导从中推导出来;然后编译失败。

作为解决方法,您可以像您所说的那样使用显式转换,或者显式指定模板参数。

F<int>(B<int>());

正如 songyuanyao 所解释的那样,模板参数 T 必须先推导。

你可以解决解释T类型的问题

F<int>(B<int>());

但我知道这可能很烦人。

因此,作为解决您的问题的方法,我建议在调用 F()

之前推导出 T 的另一个模板函数
template <template <typename> class C, typename T>
void G (C<T> const & c)
 { F<T>(c); }

因此您可以调用 F(),通过 G(),无需解释 T 类型

G(B<int>{});