簡體   English   中英

使用模板進行隱式類型轉換

[英]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::maxstd::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+55+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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM