解决方案已经在
这个答案中展示。现在,更多关于问题的内容...
你的代码中的问题在于重载分辨率的执行方式。当考虑模板函数进行重载分辨率时,编译器将对参数执行类型推导,并提供与调用匹配的类型替换,否则它将无法应用该模板,将其从潜在候选集中移除并继续执行。此时的问题是类型推导仅推导出精确匹配(可能带有额外的const/volatile限定符)。由于匹配是精确的,编译器将不使用任何转换(除了cv之外)。
最简单的例子是
std::max
和
std::min
函数:
unsigned int i = 0
std::min( i, 10 )
类型推导会将 template <typename T> min( T const &, T const & )
中的第一个参数推断为 unsigned
,而将第二个参数推断为 int
。由于它们不同,编译器将放弃这个模板函数。
在答案中提出的解决方案是使用语言的一种特性,使您能够在类定义内部定义非成员友元函数。对于每个(不同的)模板实例化,模板的优势在于编译器将创建一个在命名空间级别上具有通过在友元声明中替换实例化的实际类型获得的签名的自由非模板函数:
template <typename T>
class test {
friend test operator+( test const & lhs, test const & rhs ) {
return test();
}
}
test<int> t;
在上面的示例中,编译器允许你在类作用域内添加友元函数的定义[1]。然后,在[2]实例化模板时,编译器将生成一个自由函数。
test<int> operator+( test<int> const & lhs, test<int> const & rhs ) {
return test<int>();
}
该函数始终被定义,无论您是否使用它(这与模板类成员函数不同,后者在需要时才实例化)。
这里的“魔法”有多个方面。第一部分是您为每个实例化类型定义非模板函数,因此您获得了通用性和同时使用此函数时重载解决能力的优势,即使参数不完全匹配。
由于它是非模板函数,编译器能够对两个参数进行隐式转换,并且您将获得预期的行为。
此外,查找时发生了不同类型的“魔法”,因为所定义的函数只能通过参数相关查找找到,除非它也在命名空间级别声明,在我们的情况下无法以通用方式完成。这可能的含义可能是好的或坏的,这取决于您如何考虑它...
因为它只能通过ADL找到,所以除非至少一个参数已经是所需类型(即执行转换到两个参数都不会使用它),否则不会考虑它。缺点是除非您实际调用它,否则无法引用该函数,这意味着您无法获得函数指针。
(有关模板友元的更多信息,请单击
此处,但请注意,在这种特殊情况下,所有其他变体都无法执行隐式转换)。
operator+
)接受一个int
的函数。 - Nima + 5
中,5
应该变成A<3>(5)
而不是A<5>(5)
。如果选择后者,那么 3 应该来自于模板参数。 - Seth CarnegieA<3>(5)
。在+
中的m
值对于两个参数是相同的,因此它应该从第一个参数中获取。 - Seagull