C++ 与模板函数交朋友,避免使用虚拟函数/抽象基

C++ 与模板函数交朋友,避免使用虚拟函数/抽象基,c++,templates,friend,C++,Templates,Friend,我想成为函数模板的朋友,并尽可能地限制模板类型 下面是一个更大层次结构的片段,例如T中的模板void Play(T&)可以是T或T的形式。对于T,这意味着T是一个类模板,我想与T专用的函数成为朋友 下面代码段的预期行为是在不生成输出的情况下成功编译/链接/执行这不应打印 #include <iostream> enum class Genre { Rock = 111, Pop = 999 }; /* this is the global interface: */ templa

我想成为函数模板的朋友,并尽可能地限制模板类型

下面是一个更大层次结构的片段,例如
T
中的
模板void Play(T&)可以是
T
T
的形式。对于
T
,这意味着T是一个类模板,我想与
T
专用的函数成为朋友

下面代码段的预期行为是在不生成输出的情况下成功编译/链接/执行
这不应打印

#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;
}
#包括
枚举类体裁{Rock=111,Pop=999};
/*这是全局接口:*/
模板无效播放(T&);
模板类歌曲{
/*与自己的球员交朋友*/
模板朋友无效播放(歌曲&);
私人:
int v=int(体裁);
};
/*所需功能分辨率:*/
模板无效播放(歌曲和d)
{ 

std::cout您可以这样做:

#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;
}
#包括
枚举类类型:int{Rock=111,Pop=999};
模板类歌曲;
/*这是全局接口:*/
模板无效播放(歌曲和);
模板
班歌
{
/*与自己的球员交朋友*/
朋友空玩(歌&);
私人:
int v=int(体裁);
};
/*所需功能分辨率:*/
模板
无效播放(歌曲与d)
{

std::cout我可以确定这里有两个问题:您的
朋友
声明拾取了错误的函数,而您的两个“不需要的”函数递归地调用它们自己

要解决第一个问题,我们需要在编译器开始查看
Song
类之前告诉编译器
Play
函数是一个模板:

/* 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);
};

现在一切都正常了!

你为什么要让你的
Play
函数递归化?你的初衷是什么?不,绝对不是你所说的递归是我试图调用想要的函数。你真的需要
Play(s)吗;
expression调用流派重载?@TartanLlama,是的,但是没有
decltype
Play
函数的调用方式如下:
Play(Tinstance)
来自另一个函数模板。非常感谢,是否不可能编写单个转发函数?对于单个转发函数,您需要的是类似于
模板void Play(Song&d){Play(d);}
。不幸的是,你不能部分地专门化函数。正如我所提到的,如果
Play
是一个函子而不是函数,你可以这样做。@perreal实际上我想出了一个方法来拥有一个转发函数。我不知道它与你的真实代码有多吻合,但它对这个例子是有效的。
template <> 
void Play<Song<Genre::Pop>> (Song<Genre::Pop>& d)
{ Play(d); }

template <> 
void Play<Song<Genre::Rock>> (Song<Genre::Rock>& d)
{ Play(d); }
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;
}