繁体   English   中英

友好使用模板函数,避免使用虚函数/抽象库

[英]Friend a template function and avoid virtual functions/abstract bases

我想成为一个功能模板的朋友,并希望尽可能地限制模板类型。

下面是从较大的层次结构,使得一个片段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;
}

您在这里有两个我可以确定的问题:您的friend声明使用了错误的函数,而您的两个“不期望”函数递归调用了自己。

要解决第一个问题,我们需要在开始查看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);
};

对于您的转发功能,我们需要使它们完全符合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 ,然后使用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;
}

现在一切正常! 演示

您可以执行以下操作:

#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只是“特定” genreSong的唯一friend

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM