2015-07-13 101 views
0

假設我有具有模板友元函數,希望能實現倍增的陣列(myarray的)值的函數模​​板類的未綁定模板友元函數的返回類型:關於模板類

​​

它適用於VS2013。然後,我將外部模板朋友函數的定義移到外面:

template<typename Val, typename Array> 
myArray<T,size> operator*(const Val &lhs, const Array &rhs){ 
    myArray<T,size> mat_t; 
    for(int i = 0;i < size; i++) 
    mat_t._array[i] = lhs * rhs._array[i]; 
    return mat_t; 
} 

這是不正確的!我想問題是與朋友函數的返回類型。但我不能弄明白。 那麼如何在類聲明之外定義像這樣的朋友模板函數呢?

+1

您有問題嗎? – user463035818

+0

模板朋友可能[出了名的困難](https://isocpp.org/wiki/faq/templates#template-friends)得到正確。發佈第二個案例的錯誤和朋友聲明。 – TartanLlama

+1

你期望T和大小是什麼?你想支持Array是什麼?也許你只想讓Array成爲另一個myArray ,在這種情況下,你應該已經指定了。但是,如果您希望函數的返回獨立於輸入類型而改變類型,那麼您需要指定它(然後不能推導出它)。 – JSF

回答

0

friend函數聲明像第一個例子是奇怪的野獸。 myArray<T,Size>的每個實例都會創建一個截然不同的friend函數,只能通過參數依賴查找找到myArray<T,Size>

一個免費的operator*沒有聲明,這種方式不行。

你可以讓你的代碼,這樣的工作:

template<class Val, class T, int size> 
myArray<T,size> operator*(const Val &lhs, const myArray<T,size> &rhs){ 
    myArray<T,size> mat_t; 
    for(int i = 0;i < size; i++) 
    mat_t._array[i] = lhs * rhs._array[i]; 
    return mat_t; 
} 

模板參數這裏所有的template<>列表中列出,他們是從參數*全部抵扣。

請記住將您的operator*放在與myArray相同的命名空間中。

friend myArray operator*(const T&lhs, const myArray &rhs){ 
    myArray mat_t; 
    for(int i = 0;i < size; i++) 
    mat_t._array[i] = lhs * rhs._array[i]; 
    return mat_t; 
} 

template朋友operator*

不過,就個人而言,我會一起去。再次,其中一個是「產生」每個myArray<T,size>,但它本身不是template。這具有某些優點,比如它對轉換構造函數更好。

再進一步,我們得到:

friend myArray& operator*=(myArray&mat, const T&scalar){ 
    for(int i = 0;i < size; i++) 
    mat._array[i] *= scalar; 
    return mat; 
} 
friend myArray operator*(const T&scalar, myArray mat){ 
    mat *= scalar; 
    return mat; 
} 
friend myArray operator*(myArray mat, const T&scalar){ 
    mat *= scalar; 
    return mat; 
} 

,我們先創建一個*=,然後在它的條款寫*。請注意,*需要按值myArray(因爲無論如何我需要返回一個副本,也可能在方法外發生),並且我支持mat*scalarscalar*matmat*=scalar

另請注意,矩陣矩陣...只是工作。

這個friend operator是一個好主意的原因是說明here。注意代碼不能編譯。現在編譯爲#define ADL to move the operator* into the class as friends。 Koenig運營商讓operator*在不作爲模板的情況下在template類上運行,並且模板的參數匹配規則很難正確得到,不會過於狹窄,過於寬泛或使用討厭的SFINAE。

template<class Array 
    class=std::enable_if_t<std::is_same<std::decay_t<Array>,myArray>> 
> 
friend Array operator*=(Array&&mat, const T&scalar){ 
    for(int i = 0;i < size; i++) 
    mat._array[i] *= scalar; 
    return std::forward<Array>(mat); 
} 

這是更復雜的,但確實有趣的事情與完美轉發(用於縮寫C++ 14):

下一步,operator*=可以通過得到改善。一個臨時的myArray得到move d到返回值(它允許參考壽命延長工作),而非臨時myArray返回一個參考。

這提供了一個原因,即使*=應該是friend。它允許r和lvalues都以相同的方法實現。

另一方面,可能你不希望*=與rvalues一起使用。在這種情況下,請使用帶有&參考類別限定符的常規方法來消除該選擇。

+1

不需要'operator * ='是一個免費的朋友函數,它可以而且應該只是一個普通的成員函數。除非你決定不公開operator * =',否則'operator *'不需要成爲朋友。 –

+0

@ArneMertz我把這種技術稱爲Koenig操作員。 [this](http://coliru.stacked-crooked.com/a/cb242df537746ab6)編譯,而[this](http://coliru.stacked-crooked。com/a/d9dfd0e3bd32b6c7)沒有,唯一的區別是我們有一個Koenig'friend operator *'的情況,另一種情況是我們有一個免費的'template operator *'。有好的理由使用'模板'類型的'朋友操作符':模板函數往往被指定不足或超出規定或有令人討厭的SFINAE要求。 '操作符* ='可能是一個古老的方法,但是完美的轉發自我也是很有用的。 – Yakk

+0

@Yakk我不能讓你的第一個例子工作。在下面的示例中,您使用非模板友元函數提供了一個很好的解決方案,但是,在定義外部函數時也存在問題。使用模板友元函數,我通過將聲明更改爲'template friend array操作符*(const Val&lhs,const Array&rhs)'並在body中定義'mat_t'鍵入'Array'。但是如何在外面定義你的非模板友元函數? – Shindou

0

Tsize是模板參數myArray。如果你定義了一個包含這些模板參數的函數,而不是定義myArray,那麼你必須再次聲明它們是模板參數,例如,

template <typename T, int size, typename Val, typename Array> 

不過,這不會幫助你多少,即使你得到它的編譯,因爲你將不能夠真正正確地調用操作,因爲編譯器無法推斷出模板參數Tsize。呼叫將不得不尷尬的東西像

auto a = operator*<double, 4>(22, someArray); 

你可能想使一個函數,只返回相同類型作爲其第二個參數 - 別的就沒有太大的意義反正。爲了避免與模板的朋友聲明困難(難以得到正確的),你與朋友們的緊耦合,可以中繼運營商到公共乘法功能,或者更好的是,運營商* =

template<typename T, int size> 
class myArray { 
    T *_array; 
public: 
    myArray() : _array(new T[size]{22}) {} 
    //... 
    template<typename Val> 
    myArray& operator*=(const Val &val) { 
    for (int i = 0; i < size; ++i) 
     _array[i] *= val ; 
    return *this; 
    } 
}; 

template <typename Val, typename T, int size> 
myArray<T,size> operator*(const Val &lhs, myArray<T, size> tmp) { 
    tmp *= lhs; 
    return tmp; 
}  

對於理由爲什麼operator*=應該是會員功能而不是免費的朋友功能請參閱this link

+0

你能演示一個隱式類型轉換爲一個'myArray&'的例子,它將它綁定到一個臨時的目錄並導致一個'friend operator * ='的問題?在它是一個左值並且只能通過Koenig查找訪問的要求之間,似乎不可能。 – Yakk

+0

@Yakk參見[這裏](http://coliru.stacked-crooked.com/a/b59b9a50abf8493d)前面的例子轉換可能會導致'operator * =' –

+0

的意外調用Y case沒有區別成員或不。 [而'Z'的情況不適用於'朋友操作符* ='(Koenig風格)。](http://coliru.stacked-crooked.com/a/fa48beae40d6a2b1)。那麼爲什麼更喜歡成員'* ='而不是Koenig風格的'friend * ='? – Yakk