[英]Implicit type conversion with template
我有一個模板class A
template <unsigned int m>
class A
{
public:
A(int) {}
};
其中有一個來自int
的構造函數。 我有一個手術:
template<unsigned int m>
A<m> operator+(const A<m>&, const A<m>&)
{
return A<m>(0);
}
但是當我打電話時:
A<3> a(4);
A<3> b = a + 5;
A<3> c = 5 + a;
我想將int
隱式轉換為A,但編譯器會拋出錯誤。
有沒有優雅的方法來啟用隱式轉換而不使用以下解決方案:
a + A<m>(5)
operator+<3>(a, 5)
解決方案已在此答案中顯示 。 現在,更多關於這個問題......
代碼中的問題是如何執行重載決策。 當考慮模板函數進行重載解析時,編譯器將對參數執行類型推導,並提出與調用匹配的類型替換,否則無法應用該模板,將其從潛在候選集中移除並繼續。 此時的問題是類型推導僅推導出完全匹配(可能具有額外的const / volatile限定)。 因為匹配是精確的,所以編譯器不會使用任何轉換(再次,除了cv)。
最簡單的例子是std::max
和std::min
函數:
unsigned int i = 0;
std::min( i, 10 ); // Error!
類型推導將推導template <typename T> min( T const &, T const & )
為第一個參數的unsigned
,但是第二個參數的int
為不同的編譯器將丟棄此模板函數。
答案中提出的解決方案是使用該語言的一個功能,使您能夠在類定義中定義非成員友元函數。 模板的優點是,對於模板的每個(不同的)實例化,編譯器將在命名空間級別創建一個免費的非模板函數,該函數具有通過替換友元聲明中的實例化的實際類型而獲得的簽名:
template <typename T>
class test {
friend test operator+( test const & lhs, test const & rhs ) { // [1]
return test();
}
}
test<int> t; // [2]
在上面的示例中,編譯器允許您在[1]的類作用域內添加友元函數的定義。 然后,當您在[2]中實例化模板時,編譯器將生成一個自由函數:
test<int> operator+( test<int> const & lhs, test<int> const & rhs ) {
return test<int>();
}
無論您是否使用該函數,都會始終定義該函數(這與根據需要實例化的模板類成員函數不同)。
這里的魔力有多方面。 第一部分是,它通常為每個和所有實例化類型定義非模板函數,因此您獲得了通用性,同時當參數不完美匹配時,重載解析的優勢是能夠使用此函數。
因為它是一個非模板函數,所以編譯器能夠在兩個參數上調用隱式轉換,並且您將獲得預期的行為。
另外,一個不同類型的魔法繼續進行查找,因為這樣定義的函數只能通過參數依賴查找找到, 除非它也在命名空間級別聲明,在我們的例子中不能以通用方式完成。 這可能是好的還是壞的,取決於你想如何考慮它......
因為它只能由ADL找到,所以除非至少有一個參數已經是所需的類型(即它永遠不會用於對兩個參數執行轉換),所以不會考慮它。 缺點是除非你實際調用它,否則不可能引用該函數,這意味着你無法獲得函數指針。
(有關模板友誼的更多信息 ,請注意,在此特定情況下,所有其他變體將無法執行隱式轉換)。
每次嘗試使用模板提供操作員都需要至少一次第二次過載。 但是你可以通過在類中定義運算符來避免這種情況:
template <unsigned int m>
class A
{
public:
A(int) {}
inline friend A operator+(const A& a, const A& b) { return A(0); }
};
適用於兩者, a+5
和5+a
。
添加此運算符
template<unsigned int m>
A<m> operator+(const A<m>&, const int&)
{
return A<m>(0);
}
或試試這個
template <unsigned int m>
class A
{
friend const A operator+(const A& a, const A& b) { return A(0); }
public:
A(int) {}
// OR FOR UNARY
// const A operator+(const A &a) const {return A(0);}
};
int main(){
A<3> a(4);
A<3> b = a + 5;
A<3> c = 5 + a;
}
您可以嘗試為A
類的模板添加一個額外的“策略”類型參數,以確定實際所需的轉換類型。 例如:
template <unsigned int m, typename ConvVal = int>
class A
{
public:
typedef ConvVal conv_val;
A(ConvVal) {}
};
template<template <unsigned int, class U> class T, unsigned int m, typename U>
T<m, U> operator+(const T<m, U>&, const T<m, U>&)
{
return T<m, U>(0);
}
template<template <unsigned int, class U> class T, unsigned int m, typename U>
T<m, U> operator+(const T<m, U>&, const typename T<m, U>::conv_val&)
{
return T<m, U>(0);
}
template<template <unsigned int, class U> class T, unsigned int m, typename U>
T<m, U> operator+(const typename T<m, U>::conv_val&, const T<m, U>&)
{
return T<m, U>(0);
}
int main()
{
A<3> a(4);
A<3> b = a + 5;
return 0;
}
現在,您的A
類將采用默認為int
類型的附加模板參數,並定義允許自動轉換的實際類型。 您只需要重載operator+
function三次,一次是沒有轉換值的版本,它將采用A<m, T>
類型A<m, T>
顯式類,而另一個版本的operator+
將采用轉換類型。 在上面的代碼中,我使用更多泛型類型對此進行了概括,以便可以使用具有適當模板簽名的任何其他類來完成此操作,並定義conv_val
typedef。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.