How can I use a private inner template class as a parameter in a template specialization?

514 Views Asked by At

While trying to get some old software to compile with clang, I encountered some code similar to the following:

class OuterClass {
private:
  template <class T>
  class InnerClass {};
};

template <class T>
class OtherClass {};


template <class T>
class OtherClass<OuterClass::InnerClass<T>> {};

My understanding of this code is that the template class OtherClass is specialized for instantiations of the private inner template class OuterClass::InnerClass.

g++-10 (version 10.2.0) seems to compile this code without any errors or warnings, but clang++-10 (version 10.0.0) complains that "'InnerClass' is a private member of 'OuterClass'" at the template specialization.

Of course, I could make InnerClass public, but, since InnerClass is private in the original code, I think this solution would not be ideal. Is there a way I could allow InnerClass to be used in the specialization of OtherClass only (perhaps with some clever use of friend)?


I thought maybe I could do something like this, (which is similar to the approach taken in this answer to a similar question but I get an error stating that "partial specialization cannot be declared as a friend."

  template <typename T>
  friend class OtherClass<InnerClass<T>>;
1

There are 1 best solutions below

5
dxiv On

The posted code appears to compile under gcc only because the template is never actually instantiated (and no code is generated), but fails if attempting to instantiate it.

For OtherClass<InnerClass<T>> instantiations to work, the inner type needs to be made accessible to the template classes. One way to do it is by declaring those as a public type in OuterClass.

#include <iostream>

template <class T>
class OtherClass;

class OuterClass {
private:
  template <class T>
  class InnerClass { };

public:
  template <class T>
  using OtherInner = OtherClass<InnerClass<T>>;
};

template <class T>
class OtherClass {
public:
  OtherClass() { std::cout << "other<T>" << std::endl; }
};

template <class T>
class OtherClass<OuterClass::InnerClass<T>> {
public:
  OtherClass() { std::cout << "other<inner>" << std::endl; }
};

int main() {
  OtherClass<char> a;                        // output:  other<T>
  OuterClass::OtherInner<int> b;             // output:  other<inner>
// OtherClass<OuterClass::InnerClass<A>> c;  // error:   InnerClass is private
}

The above assumes that OuterClass knows about the OtherClass templates that it wants to expose InnerClass to. Probably a reasonable assumption in the context, otherwise InnerClass could simply be made public and accessible to all other classes/templates indiscriminately.