将模板函数加好友并避免虚拟 functions/abstract 碱基
Friend a template function and avoid virtual functions/abstract bases
我想加一个函数模板,想尽量限制模板类型。
下面是来自更大层次结构的片段,因此 template <class T> void Play(T&);
中的 T
可以是 T
或 T<U>
的形式。在 T<U>
的情况下,这意味着 T 是一个 class 模板,我想与专门用于 T<U>
的函数成为朋友。
下面代码段的预期行为是成功的 compilation/linking/execution,但没有产生输出 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;
}
你可以这样做:
#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
只是 'specific' genre
的 Song
中的 friend
。
你这里有两个我可以确定的问题:你的 friend
声明选择了错误的函数,你的两个 "not desired" 函数递归地调用它们自己。
要解决第一个问题,我们需要在编译器开始查看 Song
class:
之前告诉编译器 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);
};
对于您的转发功能,我们需要使它们完全专业化 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
,则进行类型特征检查,然后 enable/disable 使用 SFINAE 的函数:
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
我想加一个函数模板,想尽量限制模板类型。
下面是来自更大层次结构的片段,因此 template <class T> void Play(T&);
中的 T
可以是 T
或 T<U>
的形式。在 T<U>
的情况下,这意味着 T 是一个 class 模板,我想与专门用于 T<U>
的函数成为朋友。
下面代码段的预期行为是成功的 compilation/linking/execution,但没有产生输出 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;
}
你可以这样做:
#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
只是 'specific' genre
的 Song
中的 friend
。
你这里有两个我可以确定的问题:你的 friend
声明选择了错误的函数,你的两个 "not desired" 函数递归地调用它们自己。
要解决第一个问题,我们需要在编译器开始查看 Song
class:
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);
};
对于您的转发功能,我们需要使它们完全专业化 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
,则进行类型特征检查,然后 enable/disable 使用 SFINAE 的函数:
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