Friend function from another namespace

/** module.h */
#pragma once

class A {
  friend void helpers::logValue(const A &);
  int _val;

public:
  A() {}
};

namespace helpers {
  static void logValue(const A &a) {
    std::cout << a._val;  // <== ERROR: '_val' is not accessible
  }
}

How do I declare the friend function in another namespace?


Solution 1:

One possible way of solving this is as shown below:

class A;//forward declaration for class A
namespace helpers{
static void logValue(const A &a); //declaration
}
///////////////////////////////////////////

class A {
    
  friend void helpers::logValue(const A &);

  int _val;
};

namespace helpers {
  static void logValue(const A &a) {
    std::cout << a._val;  // works now
  }
}

The output of the above program can be seen here.

Solution 2:

To come up with a solution, it is necessary to work backward.

We want this to work with _val a private member of class A.

namespace helpers {
    static void logValue(const A &a) {
       std::cout << a._val;
    }
}

Step 1 For the function definition above to work there must be a preceding definition of class A visible the compiler, and that class definition must specify that helpers::logValue() (with the appropriate signature i.e. return type and arguments) is a friend. So before the definition above of helpers::logValue() we must place;

class A {
    friend void helpers::logValue(const A &);

    int _val;
};

Step 2 For that friend declaration in Step 1 to be valid, the compiler must know about helpers::logValue(). That requires a declarative region of namespace helpers visible to the compiler that declares a function logValue() with the same signature, and that must be before the definition of A. So, before our definition of class A, we must do something like;

namespace helpers{
   static void logValue(const A &a);
}

Step 3 The compiler will choke on this declaration of helpers::logValue() if there is no declaration of class A visible. We already have a class definition (created in Step 1), so we cannot create another one (that would break the one-definition rule). But we can add a declaration before the code from Step 2.

class A;

Putting those steps together, we put the code from the steps together in reverse order (i.e. put the code from Step 3 first, follow up with the code from Step 3, etc). We then get

// Step 3
class A;

// Below Step 3 comes code from Step 2

namespace helpers{
   static void logValue(const A &a);    // works since A is declared
}

// Below Step 2 comes code from Step 1

class A {
    friend void helpers::logValue(const A &);   // works since helpers::logValue() is declared

    int _val;
};

//  And now we come to the function definition we want to have work

namespace helpers {
    static void logValue(const A &a) {
       std::cout << a._val;
    }
}