2015-04-23 84 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的。