简体   繁体   中英

Declare a member-function of a forward-declared class as friend

Is it possible to declare a member function of a forward-declared class as friend? I am trying to do the following:

class BigComplicatedClass;

class Storage {
   int data_;
public:
   int data() { return data_; }
   // OK, but provides too broad access:
   friend class BigComplicatedClass;
   // ERROR "invalid use of incomplete type":
   friend void BigComplicatedClass::ModifyStorage(); 
};

So the goal is to (i) restrict the friend declaration to a single method, and (ii) not to include the definition of the complicated class to reduce compile time.

One approach might be to add a class acting as an intermediary:

// In Storage.h:
class BigComplicatedClass_Helper;
class Storage {
    // (...)
    friend class BigComplicatedClass_Helper;
};

// In BigComplicatedClass.h:
class BigComplicatedClass_Helper {
     static int &AccessData(Storage &storage) { return storage.data_; }
     friend void BigComplicatedClass::ModifyStorage();
};

However, this seems a bit clumsy... so I assume that there must be a better solution!

As @Ben says, it's not possible, but you can give specific access just to that member function through a "passkey" . It works a bit like the intermediate helper class, but is imho clearer:

// Storage.h
// forward declare the passkey
class StorageDataKey;

class Storage {
   int data_;
public:
   int data() { return data_; }
   // only functions that can pass the key to this function have access
   // and get the data as a reference
   int& data(StorageDataKey const&){ return data_; }
};

// BigComplicatedClass.cpp
#include "BigComplicatedClass.h"
#include "Storage.h"

// define the passkey
class StorageDataKey{
  StorageDataKey(){} // default ctor private
  StorageDataKey(const StorageDataKey&){} // copy ctor private

  // grant access to one method
  friend void BigComplicatedClass::ModifyStorage();
};

void BigComplicatedClass::ModifyStorage(){
  int& data = storage_.data(StorageDataKey());
  // ...
}

No, you can't declare individual member functions as friends until they've been declared. You can only befriend the entire class.

It may or may not be relevant here, but it is useful to remind ourselves that there is a wild world beyond the scope of classes and objects where functions can roam free.

For example, I recently needed to close off a (singleton global static) system error log from a global exception handler based on a port of someone else's code. The normal include file for my error log conflicted with the exception handler code because both wanted to include "windows.h" for reasons I didn't look into. When this and other questions persuaded me I could not make a forward declaration of my ErrorLog class's member functions, what I did was wrap the necessary functions into a global scope function like this:

void WriteUrgentMessageToErrorLog( const char * message )
{
  ErrorLog::LogSimpleMessage( message );
  ErrorLog::FlushAccumulatedMessagesToDisk();
}

Some people are very particular about maintaining the integrity of their class structure at all cost... and seldom acknowledge that applications using those classes are inevitably built on top of something that lacks that structure. But it's out there, and used judiciously, it has its place.

Given the age of this question, I have not looked deeply into its relevance here. All I wanted to share was the opinion that sometimes a simple wrapping mechanism like this is a much cleaner and more readily understood alternative to something that has a lot more subtlety and cleverness about it. Subtlety and cleverness tends to get changed at some later date by someone required to add to it who didn't fully understand it. Before you know it, you have a bug...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM