我对模板和非模板函数之间的c ++重载解析感到困惑,以下是示例:
class Stream {};
struct M
{
M(float v) {}
};
template <class T>
Stream& operator<<(Stream& stream, T& v) {}
Stream& operator<<(Stream& stream, const M& v) {}
int main()
{
Stream stream;
int a = 1;
stream << a; // sample 1
stream << a * a; // sample 2
return;
}
在这里,示例1调用了模板函数。样本2提供了一个int&&
类型的参数,该参数可以隐式转换为const M&
,调用非模板的参数,然后调用带有T = const int
的模板的参数。
样本2的过载分辨率会发生什么?
答案 0 :(得分:5)
这确实与模板无关。在
stream << a * a;
a * a
实现一个右值。由于您的模板函数采用T&
,因此它无法绑定到临时模板,因此将其作为可行的重载丢弃。
这将为您提供用户定义的M
转换和
Stream& operator<<(Stream& stream, const M& v)
是唯一可行的重载。
如果您更改模板以使用转发参考,例如
template <class T>
Stream& operator<<(Stream& stream, T&& v)
然后在两种情况下都将被调用,因为您将获得完全匹配。请注意,您应该使用SFINAE约束T
,否则,此重载将非常适合所有应用。
您还可以使用
template <class T>
Stream& operator<<(Stream& stream, const T& v)
答案 1 :(得分:2)
一个模板使用T&
接受参数,即对非常量的左值引用。 a * a
是一个右值,不能绑定到非常量的左值引用(顺便说一句,它可以绑定到左值对const或右值引用)。这样就不会考虑模板一。
答案 2 :(得分:2)
与左值一起使用时,模板是更好的匹配,因为它不需要调用从int
到M
的转换。
但是,由于非常量左值引用不能绑定到右值(通过乘积产生),因此无法使用此重载,因此编译器会退回转换。