2012-02-17 21 views
1

考慮以下代碼:如何在朋友功能模板的類定義的情況下避免重新定義錯誤?

template<typename T> 
class Base 
{ 
    template<typename U> 
    friend void f(void *ptr) { 
    static_cast<Base<U>*>(ptr)->run(); 
    } 
    protected: 
     virtual void run() = 0; 
}; 

class A : public Base<A> 
{ 
    protected: 
     virtual void run() {} 
}; 

/* 
class B : public Base<B> 
{ 
    protected: 
     virtual void run() {} 
}; 
*/ 

它編譯罰款現在(ideone)。但是,如果我取消的B定義,那麼它提供了以下錯誤(ideone):

prog.cpp: In instantiation of ‘Base<B>’: 
prog.cpp:20: instantiated from here 
prog.cpp:6: error: redefinition of ‘template<class U> void f(void*)’ 
prog.cpp:6: error: ‘template<class U> void f(void*)’ previously defined here 

我知道(好,我想我知道)爲什麼它給這個錯誤的原因。

所以我的問題是:

如何避免在類友元函數模板的定義的情況下重新定義錯誤?

只要我在類中提供主模板(非特殊化)的定義,我會得到這個錯誤。也有以這種方式定義主模板另一個問題:它使Base類模板所有實例,這也是我想避免的f函數模板friend的所有實例。我要讓f<T>Base<T>的朋友而不是f<U>Base<T>如果UT不一樣的朋友。同時,我也想在課堂上提供定義。可能嗎?

+0

我不明白爲什麼編譯器應該出錯了那麼一點,似乎鐺人不明白了一個道理也一樣,它編譯與鐺。 – PlasmaHH 2012-02-17 09:25:32

+0

如果我取消註釋「B」的定義,則GCC和MSVC10都會給出錯誤。 – Nawaz 2012-02-17 09:44:18

+0

@PlasmaHH:因爲朋友函數不是成員函數,因此可以不依賴於類模板的參數。 – 2012-02-17 10:49:06

回答

4

你真的需要定義f到編譯班上?如果外面定義它,你的問題就消失了,你也可以強制你想要的一對一關係(即只f<T>Base<T>的朋友):

template <typename T> class Base; 

template <typename U> 
void f(void *ptr) { 
    static_cast<Base<U>*>(ptr)->run(); 
} 

template<typename T> 
class Base 
{ 
    friend void f<T>(void *ptr); //only one instanciation is a friend 

    protected: 
    virtual void run() = 0; 
}; 

但是,請注意這樣一個事實,只有f<T>是的Base<T>朋友不會阻止下面的代碼進行編譯:

B b; 
f<A>(&b); // compiles, f<A> calls Base<A>::run, but the cast is wrong 
1

友元函數是一個全球性的功能,即使你把它落實到任何一類的機構。問題是,當你(在任何情況下)實例Base<T>兩次你提供的f兩種實現方法。請注意,f取決於T,並且它不能使用T;這對所有的Base<T>都是一樣的功能。

一個簡單的解決方案是隻提供類模板內的f聲明和實現外面:

template<typename T> 
class Base 
{ 
    template<typename U> 
    friend void f(void *ptr); 
    protected: 
    virtual void run() = 0; 
}; 


template<typename U> 
void f(void *ptr) { 
    static_cast<Base<U>*>(ptr)->run(); 
} 

class A : public Base<A> 
{ 
protected: 
    virtual void run() {} 
}; 

class B : public Base<B> 
{ 
protected: 
    virtual void run() {} 
}; 

int main() { 
} 

上面的代碼與我的G ++

+0

我知道它的工作原理(我已經試過了),我在帖子中說過我不想這樣做。 – Nawaz 2012-02-17 10:36:24

+0

由於您提供的限制條件,我認爲您可以這麼做,因爲我在第一段中解釋過。 – CygnusX1 2012-02-17 10:39:18