模板化重载运算符解析,无隐式转换
Templated overloaded operator resolution, no implicit cast
我有以下代码,带有一组模板化 类 和重载的 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
相匹配。据我所知,允许编译器进行一次隐式转换来进行重载解析,但由于某种原因,这并没有发生。有人可以解释一下为什么吗?
如果我删除所有模板,代码编译正常。
执行模板参数推导时不考虑隐式转换。即使使用您提供的构造函数,编译器也无法从 A<U>
中推断出 B<T>
中的 T
。想一想:如果我们不知道 T
是什么,我们如何将 A<int>
转换为 B<T>
?
如果模板参数未被推导但已知,则隐式转换序列可以通过转换构造函数发生。这可以通过显式提供模板参数 (operator+<int>(a1,a2)
) 来完成,但这不是最佳解决方案。另一种解决方案可以通过让 A
成为 B
.
的派生 class 来实现
template <typename T>
class B
{
};
template <typename T>
class A : public B<T> { };
现在因为 A<T>
is-a B<T>
可以发生标准的派生到基础的转换并且 T
可以推导出参数的类型。
一般来说,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+
和你写的没什么区别。它需要从 A<index<3>>
中找到一个 B<T>
来从 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);
}
映射是由我们明确完成的。
我有以下代码,带有一组模板化 类 和重载的 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
相匹配。据我所知,允许编译器进行一次隐式转换来进行重载解析,但由于某种原因,这并没有发生。有人可以解释一下为什么吗?
如果我删除所有模板,代码编译正常。
执行模板参数推导时不考虑隐式转换。即使使用您提供的构造函数,编译器也无法从 A<U>
中推断出 B<T>
中的 T
。想一想:如果我们不知道 T
是什么,我们如何将 A<int>
转换为 B<T>
?
如果模板参数未被推导但已知,则隐式转换序列可以通过转换构造函数发生。这可以通过显式提供模板参数 (operator+<int>(a1,a2)
) 来完成,但这不是最佳解决方案。另一种解决方案可以通过让 A
成为 B
.
template <typename T>
class B
{
};
template <typename T>
class A : public B<T> { };
现在因为 A<T>
is-a B<T>
可以发生标准的派生到基础的转换并且 T
可以推导出参数的类型。
一般来说,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+
和你写的没什么区别。它需要从 A<index<3>>
中找到一个 B<T>
来从 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);
}
映射是由我们明确完成的。