我有以下代码,带有一组模板化类和operator +重载:
template <typename T>
class A { };
template <typename T>
class B
{
public:
B(const A<T>& a) { }
};
template <typename T>
void operator+(B<T> lhs, B<T> rhs) { /* ... */ }
int main(/* ... */)
{
A<int> a1, a2;
a1 + a2;
return 0;
}
Clang忽略operator+(...)
作为候选人,因为它表示B
与A
无法匹配。据我所知,允许编译器执行一次隐式转换来执行重载解析,但由于某种原因,这种情况不会发生。有人能解释我为什么吗?
如果删除所有模板,代码编译正常。
答案 0 :(得分:2)
执行模板参数推断时不考虑隐式转换。即使使用您提供的构造函数,编译器也无法从T
推导出B<T>
中的A<U>
。想一想:如果我们不知道A<int>
是什么,我们如何将B<T>
转换为T
?
如果模板参数没有被推导但是已知,那么隐式转换序列可以通过转换构造函数发生。这可以通过显式提供不是最佳解决方案的模板参数(operator+<int>(a1,a2)
)来完成。另一个解决方案可以使A
成为B
的派生类。
template <typename T>
class B
{
};
template <typename T>
class A : public B<T> { };
现在,由于A<T>
是-a B<T>
,可以进行标准的派生到基础转换,并且T
可以通过参数的类型推断出来
答案 1 :(得分:1)
通常,C ++试图不使编译器反转可能完成整个过程。
template<size_t I> struct index {};
template<class T> struct A {};
template<class T> struct B {};
template<size_t I> struct B< index<I> > {
B( A< index+1 > ) {};
};
template<class T>
void operator+( B<T>, B<T> ) {}
int main() {
A< index<3> > one, two;
one+two;
}
就编译器而言,operator+
与您编写的文本没有什么不同。它需要从B<T>
中找到A<index<3>>
才能转换A
。
现在,我知道并且您知道T
的答案是index<2>
,就像在您的情况下T
的答案是int
一样,但在一般情况下所需的映射可能是非单射的(因此不是唯一可逆的),或图灵完成(因此需要解决暂停问题才能反转)。
因此,为了防止编译器(通常情况下)解决不可能出现的问题,编译器在进行模板函数类型推导时会对参数类型和参数的基本类型进行模式匹配。别无其他。
我们可以添加类型映射:
template<class T>
struct get_B_type {};
template<class T>
struct get_B_type<A<T>> {
using type=B<T>;
};
template<class T>
using get_B_type_t = typename get_B_type<T>::type;
template <class T, class U,
void_t<get_B_type_t<T>>* = nullptr,
void_t<get_B_type_t<U>>* = nullptr
>
void operator+(T lhs, U rhs) {
return (get_B_type_t<T>)(lhs) + (get_B_type_t<U>)(rhs);
}
我们明确地完成了映射。