请考虑以下代码:
b = a;
如您所见,赋值运算符已注释掉。但是,行
a
编译好。我认为它不应该编译,因为b
和operator=(const X&)
具有不同的类型,并且编译器默认生成的X<int>
将由底层类型实例化,因此它赢了&# 39;将X<double>
分配给a
。
令我惊讶的是,代码编译,似乎正在调用模板化的复制构造函数。为什么是这样?是因为编译器首先尝试将b
强制转换为B::operator=
,然后调用默认生成的=SUMPRODUCT(B3:B25,--(AD3:AD25))
?
答案 0 :(得分:3)
如果T
和U
是不同的类型,template<class U> X(const X<U>&)
不是复制构造函数,因为它的参数类型不同。换句话说,X<int>
和X<double>
是不相关的类型,因此这个构造函数只是它们之间的用户定义转换。
请注意,此代码不会打印任何内容:
X<int> a;
X<int> b { a };
因为在这种情况下,将调用X<int>::X(const X<int>&)
形式的隐式声明的复制构造函数。
答案 1 :(得分:1)
编译器正在生成对X<double>(const X<int>&)
的调用,以将X<int>
转换为X<double>
。然后,它调用生成的赋值运算符X<double>& X<double>::operator =(const X<double>&);
来进行赋值。
如果你明确写出了所有步骤,那就是:
b.operator =(X<double>(a));
答案 2 :(得分:1)
template<class U> X(const X<U>&)
是一种广义的用户定义转换 - 请参阅Anton Savin的回答。
隐式类型转换的规则非常复杂,因此您应该警惕用户定义的转换函数(More Effective C++ Item 5)。模板函数更是如此。
代码
#include <iostream>
template<class T>
struct X
{
X() = default;
template<class U>
X(const X<U>&)
{
std::cout << "generalized ctor: " << __PRETTY_FUNCTION__ << std::endl;
}
/*
template<class U>
X& operator=(const X<U>&)
{
std::cout << "generalized assignment: " << __PRETTY_FUNCTION__ << std::endl;
return *this;
}
*/
};
int main()
{
X<int> a;
X<double> b;
b = a;
}
返回
广义ctor:X :: X(const X&amp;)[U = int; T =双倍]
但是operator=
没有注释掉,它会返回
广义任务:X&amp; X :: operator =(const X&amp;)[U = int; T =双倍。
因此,您是正确的,隐式生成的operator=(const X&)
将由基础类型实例化,并且不会将X<int>
分配给X<double>
。正如Scott Meyers(参见上面的参考资料)所解释的那样,您的编译器面临对b.operator=
的{{1}}的调用,
X<double>
,并发现不存在此类函数。因此,您的编译器会尝试查找可应用的隐式类型转换的可接受序列,以使调用成功,see rules in the cpp reference。您的编译器会找到您的通用用户定义转换,并将const X<double>
转换为X<int>
,使其具有X<double>
的正确参数类型。