2015-04-23 95 views
2

我想與一個功能模板交朋友,並希望儘可能限制模板類型。朋友模板功能,避免虛擬功能/抽象基地

下面是一個較大的層次結構的片段,Ttemplate <class T> void Play(T&);可以是TT<U>的形式。如果是T<U>,這意味着T是一個班級模板,我想與T<U>專門的功能交朋友。

以下片段的預期行爲是成功編譯/鏈接/執行,而不產生輸出This should not be printed

#include <iostream> 

enum class Genre { Rock = 111, Pop = 999 }; 

/* this is the global interface: */ 
template <class T> void Play(T&); 

template <Genre genre> class Song { 
    /* befriend own player */ 
    template <class T> friend void Play(Song<genre>&); 
private: 
    int v = int(genre); 
}; 

/* desired function resolution: */ 
template <Genre genre> void Play(Song<genre>& d) 
{ 
    std::cout << "Genre: " << d.v << std::endl; 
} 

template <class T> void Play(T& d) 
{ 
    std::cout << "This should not be printed" << std::endl; 
} 

/* these two functions are not desired but I tried... */ 
template<> inline void Play(Song<Genre::Pop>& d) 
{ Play<Genre::Pop>(d); } 

template<> inline void Play(Song<Genre::Rock>& d) 
{ Play<Genre::Rock>(d); } 

int main(int argc, char *argv[]) { 
    Song<Genre::Pop> s; 
    Song<Genre::Rock> p; 
    Play<decltype(s)>(s); 
    Play(s); 
    Play(p); 
    return 0; 
} 
+0

爲什麼你讓你的'Play'函數專業化遞歸?你的初衷是什麼? – Abhijit

+0

絕對不是你所謂的遞歸是我試圖調用所需的函數。 – perreal

+0

你真的需要'玩(s);'表達式來稱呼類型超負荷? – TartanLlama

回答

2

您可以在這裏找到兩個問題,我可以識別:您的friend聲明正在拾取錯誤的函數,而您的兩個「不需要的」函數遞歸調用它們自己。

要解決的第一個,我們需要告訴編譯器這個Play功能是一個模板就開始盯着Song課前:

/* this is the global interface: */ 
//need to forward declare the Song template class 
template <Genre genre> class Song; 

//forward declare the version of Play templated on Genre 
template <Genre genre> 
void Play(Song<genre>&); 

//keep the version you had originally 
template <typename T> 
void Play(T&); 

template <Genre genre> class Song { 
    /* befriend own player */ 
    //now picks up the correct function 
    friend void Play <> (Song<genre>&); 
private: 
    int v = int(genre); 
}; 

爲了您的轉發功能,我們需要讓他們充分專業化的template <typename T> Play(T&>版本:

template <> 
void Play<Song<Genre::Pop>> (Song<Genre::Pop>& d) 
{ Play(d); } 

template <> 
void Play<Song<Genre::Rock>> (Song<Genre::Rock>& d) 
{ Play(d); } 

另一種方法是使你在一個Song過去了,然後啓用/禁用與SFINA功能的類型特徵檢查E:

template <class T> 
struct is_song : std::false_type {}; 

template <Genre genre> 
struct is_song<Song<genre>> : std::true_type {}; 

template <typename T, std::enable_if_t<is_song<T>::value>* = nullptr> 
void Play (T& d) 
{ Play(d); } 

template <typename T, std::enable_if_t<!is_song<T>::value>* = nullptr> 
void Play(T& d) 
{ 
    std::cout << "This should not be printed" << std::endl; 
} 

現在一切正常! Demo

+0

非常感謝,是不是可以寫一個單一的轉發功能? – perreal

+1

你想要一個轉發功能就像'template void Play >(Song &d) {Play(d); }'。不幸的是,你不能部分地專門化功能。正如我所提到的,如果'Play'是函數而不是函數,那麼可以這樣做。 – TartanLlama

+0

@perreal其實我想出了一種方法來有一個單一的轉發功能。我不知道它是否適合你的真實代碼,但它適用於這個例子。 – TartanLlama

1

你可以做這樣的事情:

#include <iostream> 

enum class Genre : int { Rock = 111, Pop = 999 }; 

template<Genre genre> class Song; 

/* this is the global interface: */ 
template <Genre genre> void Play(Song<genre>&); 

template <Genre genre> 
class Song 
{ 
    /* befriend own player */ 
    friend void Play<>(Song<genre>&); 

    private: 
     int v = int(genre); 
}; 

/* desired function resolution: */ 
template <Genre genre> 
void Play(Song<genre>& d) 
{ 
    std::cout << "Genre: " << d.v << std::endl; 
} 

/* non-desired function */ 
template <class T> 
void Play(T& d) 
{ 
    std::cout << "This should not be printed" << d.v << std::endl; 
} 

int main(int argc, char *argv[]) 
{ 
    Song<Genre::Pop> s; 
    Song<Genre::Rock> p; 
    //Play<decltype(s)>(s); // <--- will not compile: calls the 'non-desired' function 
          // <--- which is not friend of Song<Genre::Pop> 
          // <--- and compilation fails as the function tries to access the private member v 
    Play(s); 
    Play(p); 
    //Play<Genre::Rock>(s); // <--- will also not compile 
    return 0; 
} 

這裏Play只是Songfriend S中的 '特定' genre的。