C++ How to specify all friends of a templated class with a default argument?

arguments, c++, friend, templates

Solution

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.

Problem

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.

Original source

Related problems