9

is it possible to somehow make a partial template specification a friend class? I.e. consider you have the following template class

template <class T> class X{
    T t;
};

Now you have partial specializations, for example, for pointers

template <class T> class X<T*>{
    T* t;
};

What I want to accomplish is that every possible X<T*> is a friend class of X<S> for ANY S. I.e. X<A*> should be a friend of X<B>.

Of course, I thought about a usual template friend declaration in X:

template <class T> class X{
    template <class S> friend class X<S*>;
}

However, this does not compile, g++ tells me this:

test4.cpp:34:15: error: specialization of 'template<class T> class X' must appear at namespace scope

test4.cpp:34:21: error: partial specialization 'X<S*>' declared 'friend'

Is this not possible at all or is there some workaround?

The reason why I am asking is that I need a constructor in X<T*> that creates this class from an arbitrary X<S> (S must be a subtype of T).

The code looks like this:

template <class T> class X<T*>{
    T* t;

    template<class S>
    X(X<S> x) : t(&(x.t))  {} //Error, x.t is private
}

Now, the compiler complains, of course, that x.t is not visibile in the constructor since it is private. This is why I need a partial specialization friend class.

gexicide
  • 38,535
  • 21
  • 92
  • 152
  • 1
    Is a `get` function really out of the question? This seems a lot cleaner to me and avoids all the template friend madness. – pmr Jun 15 '12 at 08:07
  • it would work in this example maybe. However, there may be data that should not be exposed to the public but only to template specializations. The question is if this behaviour is possible somehow. – gexicide Jun 15 '12 at 08:13

2 Answers2

8

In C++, you can grant access beyond private on four levels.

  • completely public access (see pmr's answer)
  • access within inheritance hierarchy (protected, irrelevant here)
  • to a base template friend (see this answer)
  • to a non-template or fully specialized friend (too weak to solve your use case)

There is no middle way between the two latter kinds of friendship.

From §14.5.4 of the C++ standard:.

Friend declarations shall not declare partial specializations.

The following declaration will allow you to implement what you need. It gives you a free hand to access any specialization of your template from any other specialization, but still only within X. It is slightly more permissive than what you asked for.

template<class T> class X
{
    template<class Any> friend class X;
    public:
        ...
};
Jirka Hanika
  • 13,301
  • 3
  • 46
  • 75
  • In case anyone else hits the same snag as me: be sure to match the syntax correctly. The following code will report error attempt to make a partial specialization a friend even if not meaning to: `template friend class X;` Notice the extra brackets at the end compared to the correct example in the answer. – mattgately Jul 16 '20 at 15:43
1

We can define a getter protected by a key defined in X.

#include <type_traits>

template <class T> class X{
  T t;
public:
  struct Key {
    template<typename S>
    Key(const X<S>&) {
      static_assert(std::is_pointer<S>::value, "Not a pointer");
    }
  };

  const T& get(Key) const { return t; }
  T& get(Key) { return t; }
};

template <class T> class X<T*> {
  T* t;
public:
  template<class S>
  X(X<S>& x) : t(&(x.get(typename X<S>::Key(*this))))  {}
};

int main()
{
  X<int> x1;
  X<int*> x2(x1);
  return 0;
}

This still has some weakness. Everybody with an X<T*> can now use get. But this is so obfuscated by now, that no one is goiing to realize that. I'd choose a simple public getter.

pmr
  • 58,701
  • 10
  • 113
  • 156