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.
Default parameters for templates in C++: Like function default arguments, templates can also have default arguments.
There are ways to restrict the types you can use inside a template you write by using specific typedefs inside your template. This will ensure that the compilation of the template specialisation for a type that does not include that particular typedef will fail, so you can selectively support/not support certain types.
A template argument for a template template parameter is the name of a class template. When the compiler tries to find a template to match the template template argument, it only considers primary class templates. (A primary template is the template that is being specialized.)
There is no semantic difference between class and typename in a template-parameter. typename however is possible in another context when using templates - to hint at the compiler that you are referring to a dependent type. §14.6.
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>>){}
};
Live example with tests.
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());
}
//...
};
Live example.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With