To define a friend of a templated class with a default argument, do you need to specify all friends as in the code below (which works)?
// Different class implementations
enum ClassImplType { CIT_CHECK, CIT_FAST, CIT_GPU, CIT_SSE, CIT_NOF_TYPES } ;
// Graph class has default template argument CIT_CHECK
template <typename T, ClassImplType impl_type = CIT_CHECK>
class graph {
//...
};
// Vertex class
template <typename T>
class vertex {
//...
friend class graph<T, CIT_CHECK>;
friend class graph<T, CIT_FAST>;
friend class graph<T, CIT_GPU>;
friend class graph<T, CIT_SSE>;
};
I can imagine that there is a shorter way to denote that the friend is defined for all possible ClassImplType enum values. Something like friend class graph<T, ClassImplType>
, but the latter doesn't work of course.
Apologies if the terminology I use is incorrect.
I can imagine that there is a shorter way to denote that the friend is defined for all possible ClassImplType enum values.
Sadly, there really isn't. You might try with
template<ClassImplType I> friend class graph<T, I>;
but the standard simply forbids one to befriend partial specializations:
§14.5.4 [temp.friend] p8
Friend declarations shall not declare partial specializations. [ Example:
template<class T> class A { }; class X { template<class T> friend class A<T*>; // error };
—end example ]
You can only either befriend them all:
template<class U, ClassImplType I>
friend class graph;
Or a specific one:
friend class graph<T /*, optional-second-arg*/>;
I can't see how befriending all possible specializations might cause a problem here, to be honest, but let's assume it does. One workaround I know would be using the passkey pattern, though we'll use a slightly cut-down version (we can't use the allow
mechanism here, since it doesn't work well for allowing access to all specializations of a template):
template<class T>
class passkey{
passkey(){}
friend T;
// optional
//passkey(passkey const&) = delete;
//passkey(passkey&&) = delete;
};
// Different class implementations
enum ClassImplType { CIT_CHECK, CIT_FAST, CIT_GPU, CIT_SSE, CIT_NOF_TYPES } ;
template<class> struct vertex;
// Graph class has default template argument CIT_CHECK
template <typename T, ClassImplType impl_type = CIT_CHECK>
class graph {
public:
void call_f(vertex<T>& v){ v.f(passkey<graph>()); }
//...
};
// Vertex class
template <typename T>
class vertex {
//...
public:
template<ClassImplType I>
void f(passkey<graph<T,I>>){}
};
You'll note that you need to make all functionality that graph
needs to access public, but that's not a problem thanks to the passkeys, which can only ever be created by the specified graph
specializations.
You can also go farther and create a proxy class which can be used to access the vertex functionality (only graph
changes):
// Graph class has default template argument CIT_CHECK
template <typename T, ClassImplType impl_type = CIT_CHECK>
class graph{
typedef passkey<graph> key;
// proxy for succinct multiple operations
struct vertex_access{
vertex_access(vertex<T>& v, key k)
: _v(v), _key(k){}
void f(){ _v.f(_key); }
private:
vertex<T>& _v;
key _key;
};
public:
void call_f(vertex<T>& v){
vertex_access va(v, key());
va.f(); va.f(); va.f();
// or
v.f(key());
}
//...
};