[轉]引用模板類中定義的類型(用typedef或using)以及auto、decltype、typename的使用


一、背景

使用typedef或者using定義類型別名是非常常見的手段,在c++里面,有時為了封裝性,模塊性等原因還會在某一個namespace或者class內部定義類型別名。

最近在寫c++代碼的時候,有實現一個模板類,說實話,雖然用c++用了好多年了,但還真沒花多少時間去研究模板,因為我始終覺得,做項目,開發軟件,不是為了炫技,我也不認為會玩兒模板就是牛人大神了,最主要的是把握好三個“用”就好了,這三個用分別是:實用,適用,夠用。

言歸正傳,這次實用模板類,也會用到在模板類里面實用typedef定義類型別名,當然,其實這也沒什么問題,只要在模板類內部實現每個模板類的成員函數,這一切都不是問題,但是我想讓模板類聲明的干凈純粹一些,換句話說,就是將模板類的成員函數全部都放到外面去實現,而不是直接的模板類內部實現,模板類只是聲明每個成員函數。

問題就來了,欲知詳情,請接着往下看。

二、解決方案

假定這里需要實現這樣一個模板myFoo, 這個模板有一個成員函數size,這個成員函數會返回一個表示size的值,其聲明大致如下:

template<typename T>
class myFoo
{
public:
    using size_type = std::size_t;

public:
    size_type size() const;
private:
    size_type mSize = 5;
};

  這里如果只是在模板類里面實現這個size成員函數的話,就不會有任何問題,形如:

template<typename T>
class myFoo
{
public:
    using size_type = std::size_t;

public:
    size_type size() const
    {
        return mSize;
    }
private:
    size_type mSize = 5;
};

  問題就是,我需要在模板類外面實現這個size成員函數,如果按照普通的c++類來實現,如下所示:

myFoo::size_type myFoo::size() const
{
    return mSize;
}

  當然,如果你這樣寫的話,編譯器是不會讓你過去的,模板類的成員函數在類外面實現自有它的規則,形如:

template<typename T>
myFoo<T>::size_type myFoo<T>::size() const
{
    return mSize;
}

  

當然,如果你走到這一步了,說明你對模板類外實現成員函數的規則算是基本了解了,但這種寫法仍然不對,也會被編譯器無情的拒絕。

因為,對於模板類,myFoo::size_type 這樣的寫法,會被認為是引用一個名為size_type的成員變量,而這里的size_type只是size_t的別名而已,是個類型名,而不是成員變量。

答案最終揭曉,正確的寫法是,保證對size_type的引用是類型名的引用,其實解決方法並不止一種:

  • 其一:使用typename關鍵字,具體代碼如下:
template<typename T>
typename myFoo<T>::size_type myFoo<T>::size() const
{
    return mSize;
}
  • 其二:使用auto和decltype關鍵字,具體代碼如下(包含模板類的聲明部分):
template<typename T>
class myFoo
{
public:
    using size_type = std::size_t;

public:
    auto size() const -> decltype(myFoo<T>::mSize);
private:
    size_type mSize = 5;
};

/** size成員函數實現 */
template<typename T>
auto myFoo<T>::size() const -> decltype(myFoo<T>::mSize)
{
    return mSize;
}

  

兩種寫法都可,選哪一種就看個人習慣和愛好了(其實:在visual studio2017里面只需要auto關鍵字就可以了,不需要decltype關鍵之,后面會說到)。

三、模板函數的返回值類型推導

這里順便說一下,模板函數的返回值類型推導, 舉一個簡單的例子,實現一個將兩個變量相加的模板函數,原型大致為:

template<typename T1, typename T2>
xxx myAdd(T1 a, T2 b);

  

之所以這里的返回值,是一個“xxx”, 是因為無法確定這個返回值類型會是什么,如果T1和T2都是相同類型如int,那么還好辦,“xxx”取T1或者T2都可以,但如果T1和T2類型不同,比如一個是int,一個double,理論上,應該返回double類型, 但是這個模板函數並不知道T1是double,還是T2是double。

這里你可以要說,這簡單啊,直接用auto關鍵字就好了,這里需要說明一下,我試驗的結果是,使用visual studio2017的話,用auto關鍵字作為這個模板函數的返回值是可以的,其形式如下:

template<typename T1, typename T2>
auto myAdd(T1 a, T2 b)
{
    return a + b;
}

  但是如果使用gcc的話,這樣寫就不行,會報如下的錯誤:

‘myAdd’ function uses ‘auto’ type specifier without trailing return type

  也就是說,需要說明模板函數返回值的推導方式,根據c++11標准,這種情況需要在模板函數后面用decltype關鍵字添加返回值類型的推導,代碼如下:

template<typename T1, typename T2>
auto myAdd(T1 a, T2 b) -> decltype(a + b)
{
    return a + b;
}

  

這種寫法在visual studio2017和gcc中都能編譯通過,這里不太清楚,是否visual studio 2017對只使用auto關鍵字的情況作了某些默認的推導。

所以為了代碼的通用性,還是使用后面這種寫法吧,這樣在使用不同編譯器時就不需要針對特定的編譯器進行代碼修改了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM