Объявить функцию-член класса с объявлением вперед в качестве друга

Можно ли объявить функцию-член объявленного вперед класса как друга? Я пытаюсь сделать следующее:

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(); 
};

Таким образом, цель состоит в том, чтобы (i) ограничивать объявление друга единственным методом и (ii) не включать определение сложного класса для сокращения времени компиляции.

Одним из подходов может быть добавление класса, действующего в качестве посредника:

// 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();
};

Однако это кажется немного неуклюжим... поэтому я предполагаю, что должно быть лучшее решение!

Ответ 1

Как говорит @Ben, это невозможно, но вы можете предоставить конкретный доступ только этой функции-члена через "passkey" . Он немного похож на промежуточный вспомогательный класс, но 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());
  // ...
}

Ответ 2

Нет, вы не можете объявлять отдельные функции-члены в качестве друзей, пока они не будут объявлены. Вы можете только подружиться со всем классом.

Ответ 3

Это может быть или не быть релевантным здесь, но полезно напомнить себе, что существует дикий мир, выходящий за рамки классов и объектов, где функции могут свободно перемещаться.

Например, недавно мне пришлось закрыть системный журнал (singleton global static) из глобального обработчика исключений на основе порта другого кода. Обычный файл include для моего журнала ошибок противоречил коду обработчика исключений, потому что оба хотели включить "windows.h" по причинам, на которые я не смотрел. Когда этот и другие вопросы уговорили меня, я не мог сделать прямого объявления функций-членов класса ErrorLog, что я сделал, это обернуть необходимые функции в глобальную функцию видимости:

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

Некоторые люди очень любят поддерживать целостность своей структуры классов любой ценой... и редко признают, что приложения, использующие эти классы, неизбежно создаются поверх чего-то, что не хватает этой структуры. Но он там и использовался разумно, он имеет свое место.

Учитывая возраст этого вопроса, я не смотрел глубоко в его значимость здесь. Все, что я хотел рассказать, заключалось в том, что иногда простой механизм обертывания, подобный этому, является гораздо более чистой и более понятной альтернативой чему-то, что имеет гораздо более тонкость и умение. Тонкость и умение, как правило, меняются в какой-то более поздний срок кем-то, кто должен добавить к нему, кто не понял его полностью. Прежде чем вы это узнаете, у вас есть ошибка...

Ответ 4

Можно ли объявить функцию-член класса, объявленного с переходом, как друга?

Нет. Конечно, компилятор не знает о таких функциях-членах.