c++ - Friend a template function and avoid virtual functions/abstract bases -
i want befriend function template , want restrict template type as possible.
below snippet larger hierarchy such t
in template <class t> void play(t&);
can of form t
, or t<u>
. in case of t<u>
, means t class template, befriend function specialized t<u>
.
the expected behavior snippet below successful compilation/linking/execution without producing output this should not printed
.
#include <iostream> enum class genre { rock = 111, pop = 999 }; /* 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 printed" << std::endl; } /* these 2 functions not desired 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; }
you have 2 issues here identify: friend
declaration picking wrong function , 2 "not desired" functions recursively call themselves.
to fix first, need tell compiler play
function template before starts looking @ song
class:
/* global interface: */ //need forward declare song template class template <genre genre> class song; //forward declare version of play templated on genre template <genre genre> void play(song<genre>&); //keep version had template <typename t> void play(t&); template <genre genre> class song { /* befriend own player */ //now picks correct function friend void play <> (song<genre>&); private: int v = int(genre); };
for forwarding functions, need make them full specializations of template <typename t> play(t&>
version:
template <> void play<song<genre::pop>> (song<genre::pop>& d) { play(d); } template <> void play<song<genre::rock>> (song<genre::rock>& d) { play(d); }
an alternative make type trait checking if have passed in song
, enable/disable function 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 printed" << std::endl; }
now works! demo