[英]Why does the compiler invoke a templated copy constructor when assigning?
考虑下面的代码:
#include <iostream>
template<class T>
struct X
{
X() = default;
template<class U>
X(const X<U>&)
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
/*
template<class U>
X& operator=(const X<U>&)
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
return *this;
}
*/
};
int main()
{
X<int> a;
X<double> b;
b = a;
}
如您所见,赋值运算符已被注释掉。 但是,线
b = a;
编译良好。 我认为它不应该编译,因为a
和b
具有不同的类型,并且编译器默认生成的operator=(const X&)
将由基础类型实例化,因此不会分配X<int>
到X<double>
。
令我惊讶的是,代码已编译,并且似乎正在调用模板化副本构造函数。 为什么是这样? 难道是因为编译器会先尝试在投a
到b
然后调用默认生成的B::operator=
?
如果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>&)
的隐式声明的副本构造X<int>::X(const X<int>&)
。
编译器正在生成对X<double>(const X<int>&)
的调用,以将X<int>
转换为X<double>
。 然后调用生成的赋值运算符X<double>& X<double>::operator =(const X<double>&);
做作业。
如果您明确列出所有步骤,它将是:
b.operator =(X<double>(a));
template<class U> X(const X<U>&)
是一种通用的用户定义转换-请参阅Anton Savin的答案。
隐式类型转换的规则非常复杂,因此您应警惕用户定义的转换函数(“ 更有效的C ++项目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&)[with U = int; T =两倍]
但是对于operator=
未注释掉),它将返回
广义赋值:X&X :: operator =(const X&)[with U = int; T = double]。
因此,您正确的是,隐式生成的operator=(const X&)
将由基础类型实例化,并且不会将X<int>
分配给X<double>
。 正如Scott Meyers(请参阅上面的参考资料)所述,您的编译器面临着对X<double>
b.operator=
的调用,该调用采用了const X<double>
,并且发现不存在这样的函数。 因此,您的编译器然后尝试找到可接受的隐式类型转换序列,该序列可用于使调用成功, 请参阅cpp参考中的规则 。 您的编译器会找到通用的用户定义转换,并将X<int>
转换为X<double>
以具有b.operator=
的正确参数类型。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.