2015-01-04 65 views
4

代碼一個明確的專業化不能成爲朋友聲明

template <typename T> 
void foo(const T& t) 
{} 

template <typename T> 
class A 
{ 
    template <> 
    friend void foo<T>(const T& t) 
    {} 
}; 

給出編譯錯誤

"defining explicit specialization ‘foo<T>’ in friend declaration friend void foo<T>(const T& t)" 

用gcc和

"error C3637: 'A<int>::foo' : a friend function definition cannot be a specialization of a unction template" 

編譯在VS2013

編譯時,當我說話那標準是這麼說的,但爲什麼?我想了解原因(內幕) 有許多文章寫着「明確的專業化不能成爲朋友聲明」,但我不明白爲什麼。有任何想法嗎?

+1

對於給定的代碼,gcc報告'在主模板的聲明中'在非名稱空間範圍'struct A '和'template-id'foo 中的顯式特化。 – user2079303

+0

對不起,我從其他來源複製粘貼 –

+0

明確的特殊化可防止隱式實例化。每次你爲一組新的模板參數實例化'A'時,你都會添加另一個明確的'foo'特化。如果'foo'已經被隱式地實例化爲相同的模板參數[temp.expl.spec]/6,那麼它是不合格的,不需要診斷。 – dyp

回答

3

在類模板中聲明第一個(也可能是唯一的)時間的顯式特化將意味着一旦模板被實例化,顯式特化只是「現有」 - 無論聲明是否依賴於模板參數或不。 這會產生很多問題,並會在各種情況下導致對ODR的違反,其中許多情況可能是不合格的NDR;這主要是因爲由@dyp評價提到的段落,[temp.expl.spec]/6

此外,朋友函數定義類內部而不外部聲明使得該功能只invokeable經由ADL。顯然,如果顯式專門化只適用於調用關聯參數類型的情況下 - 絕對不會提及ODR的違規行爲,那麼這將是絕對沒有意義的。

這些和其他原因使得這樣的構造太複雜太容許,但不是非常有益的:你可以簡單地做的是將專業化添加爲friend,而不是以任何方式指示該專業化是實例化還是明確專門化。

friend void foo<T>(const T&); 

然後,可以在命名空間範圍內添加任何明確的特化。

+0

或者你可以重載,通過在裏面定義一個非模板的朋友函數。 – dyp

+0

爲了完整性,類中的朋友函數定義通常允許在不使用ADL的情況下調用該函數,而不使用類內聲明。只要類外聲明可用,就沒有問題,所以:struct S {friend void f(){}} void f(); int main(){f(); }'是完全有效的。但是,明確的模板專業化需要使用明確的專業化,因此,類內聲明不夠好。 – hvd

+1

@Columbo謝謝你的回答,但你能否更清楚地解釋ADL的情況,我不明白這個問題。如果在類中定義了函數模板特化,那麼它應該接受一個類的派生參數或派生自類的類,因爲ADL機制應該起作用。那麼這種(類或派生)參數的模板專門化定義有什麼問題? –