2017-03-15 39 views
2

這在過去的幾個小時裏一直令我瘋狂,我似乎無法解決這個問題。我將這個問題歸結爲這60行代碼(包括一個主函數)。在另一個命名空間和CRTP中的模板好友功能

#include <iostream> 

namespace n1 { 

// the general definition 
template <class X, class Y> void f(X&, const Y&) 
{ 
    std::cout << "general template definition.\n"; 
} 

} // namespace n1 
namespace n2 { 

// CRTP 
template <class Derived> class A 
{ 
    int data; 

    // partial function template specialization for n1::f, and declare 
    // it a friend too, so that it may access the data attribute of A 
    template <class Y> friend void n1::f(A<Derived>& a, const Y& y); 

}; // class A 

} // namespace n2 
namespace n1 { 

// implementation for this particular function template specialization 
template <class Derived, class Y> void f(n2::A<Derived>& a, const Y& y) 
{ 
    std::cout << "partial template specialization: " << a.data << "\n"; 
} 

} // namespace n1 
namespace n2 { 

// Another class! 
class B : public A<B> 
{ 

}; // class B  
} // namespace n2 
namespace n1 { 

// -------------------- 
// tricky part is here! 
// -------------------- 
template <class Y> void f(n2::B& b, const Y& y) 
{ 
    // FAIL! not a friend! How? 
    f(static_cast<n2::A<n2::B>&>(b), y); 
} 

} // namespace n1 

int main() 
{ 
    n2::B b; 
    int x; 
    n1::f(b, x); // should print "partial template specialization" 
    return 0; 
} 

所以,我「想」是可以讓編譯器選擇n1::f我的函數模板專業化每當這與一種A<Derived>具體子類調用。爲了確保編譯器支持我的專業化,我需要爲每個子類(在這種情況下爲B)提供一個n1::f的模板專用,它可以簡單地委託調用。當發生這種情況時,我預計A<Derived>data成員變量可被n1::f訪問,因爲我聲明n1::fA<Derived>的朋友。但是,海灣合作委員會抱怨A<Derived>::data是私人的,無法訪問,see this snippet on Coliru

這是可行的嗎?如果是這樣,我怎麼能繞過編譯器抱怨A<Derived>::data不可訪問? (讓它公開不是一種選擇)。

回答

3

您的類定義必須是這樣的:

template <class Derived> class A 
{ 
    int data; 
    template <class D, class Y> friend void n1::f(A<D>& a, const Y& y); 
}; 

事實上,函數的聲明是:

template <class Derived, class Y> void f(n2::A<Derived>& a, const Y& y) 

當你的朋友聲明:

template <class Y> friend void n1::f(A<Derived>& a, const Y& y); 

在這種情況下,他們是不同的動物,這就是爲什麼你會收到這個錯誤。如您所見,模板參數列表不同。這不是一個具有分離定義的函數聲明。它們是兩個不同的函數模板,一個聲明,另一個聲明和定義。
換句話說,在你的代碼中你聲明瞭一個朋友函數,但你永遠不會定義它。另一方面,您介紹了一個免費函數模板,該模板無法讀取data成員,因爲它是私有的,並且該函數不是A<Derived>的朋友之一。


看到它在wandbox上運行。

+0

我看到...這個工程!哇。你能解釋爲什麼額外的模板參數'D'是必需的嗎?它在我看來像我的朋友聲明會使任何'A '成爲朋友,但似乎你需要一個單獨的模板參數。 – rwols

+0

@rwols我增加了更多細節。它們是兩個不同的函數模板,因爲它們具有不同的模板參數列表和不同的參數。我不知道如何更好地解釋它... – skypjack

+0

謝謝,我認爲這爲我解決了一些問題。 – rwols