[英]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.