c++templatesnon-type-template-parameterfriend-class

How to have a template class using non-type template parameter "friend" another template class using the same non-type template parameter?


NOTE: The original close reason for this question WAS in fact resolved, as the note I added (immediately below this) does in fact explain how this question is different from the one it's claimed as a duplicate of, and the answer to this question, which was provided before the question was closed, answers this specific question and not the other one.

NOTE: I do not believe this is a duplicate (as marked) of Class template with template class friend, what's really going on here? because that problem was due to the use of the same template parameter name in a NESTED template, where the inner use of the name hid the outer use. In this question the templates are not nested, and the there is not a problem with the use of the same template parameter name.

Is it possible for a template class that takes a non-type template parameter to 'friend' another template class taking an non-type template parameter? What's the syntax for doing that?

I tried this:

#include <iostream>

struct Configuration
{
  static constexpr int foo = 12;
};

// forward declaration of template class Big
template <Configuration config>
class Big;

template <Configuration config>
class Small
{
public:
  Small(Big<config>& big):
    big(big)
  {
  }

  void doit()
  {
    std::cout << big.burger + config.foo << "\n";
  }

protected:
  Big<config>& big;
};

template <Configuration config>
class Big
{
  template <config> friend class Small;

public:
  Big():
    small(*this)
  {
  }

  void doit()
  {
    small.doit();
  }

  protected:
  Small<config> small;
  double burger = 42.3;
};

int main([[maybe_unused]] int argc,
     [[maybe_unused]] char *argv[])
{
  Configuration conf;
  Big<conf> big;
  big.doit();
}


GCC 14.0.1 using --std=c++20 reports:

friend-template-class.cc:31:11: error: 'config' is not a type
   31 | template <config> friend class Class1;
      |           ^~~~~~

and of course

friend-template-class.cc:23:22: error: 'double Big<Configuration()>::burger' is protected within this context
   23 |     std::cout << big.burger + config.foo << "\n";
      |                  ~~~~^~~~~~
friend-template-class.cc:48:10: note: declared protected here
   48 |   double burger = 42.3;
      |          ^~~~~~

To move forward, I commented out the friend declaration and the "protected:", and then it performs as expected, but I really would prefer to keep the access specifier.


Solution

  • You can either befriend a specific specialization of a class template, or every specialization of the class template:

    // befriend all specializations
    template <Configuration> friend class Small;
    
    // befriend specific specialization
    friend class Small<config>;