Подтвердить что ты не робот

Можно ли объявить функцию друга статичной?

Вот пример кода кода С++, который компилируется и работает нормально:

class A
{
public:
   A() {/* empty */}

private:
   friend void IncrementValue(A &);
   int value;
};

void IncrementValue(A & a)
{
   a.value++;
}   

int main(int, char **)
{
   A a;
   IncrementValue(a);
   return 0;
}

Однако я хотел бы сделать объявление IncrementValue() статическим, чтобы его нельзя было увидеть или вызвать из другого модуля компиляции:

static void IncrementValue(A & a)
{
    a.value++;
}

Выполнение этого, однако, дает мне ошибку компиляции:

temp.cpp: In function ‘void IncrementValue(A&)’:
temp.cpp:12: error: ‘void IncrementValue(A&)’ was declared ‘extern’ and later ‘static’
temp.cpp:8: error: previous declaration of ‘void IncrementValue(A&)’

... и изменение объявления друга для соответствия не помогает:

friend static void IncrementValue(A &);

... поскольку он дает эту ошибку:

temp.cpp:8: error: storage class specifiers invalid in friend function declarations

Мой вопрос в том, есть ли способ в С++ иметь (не-метод) функцию друга, объявленную как static?

4b9b3361

Ответ 1

Цитата N3691 - §11.3/4 [class.friend]

Функция, объявленная в объявлении друга, имеет внешнюю связь (3.5). В противном случае функция сохраняет прежнюю связь (7.1.1).

Поэтому вам нужно объявить функцию как static до объявления ее как friend. Это можно сделать, добавив следующие объявления выше определения A.

class A;  // forward declaration, required for following declaration
static void IncrementValue(A&); // the friend declaration will retain static linkage

Ответ 2

Конечно. Внимательно прочитайте вторую строку сообщения об ошибке: функция была объявлена ​​ extern и позже static. Итак, все, что вам нужно сделать, это объявить его статическим перед объявлением друга:

class A;
static void IncrementValue(A&);

class A {
    // class definition, including friend declaration
};

static void IncrementValue(A&) {
    // code here, of course
}

Ответ 3

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

Есть две проблемы с его решением. Во-первых, любая другая единица перевода, которая включает заголовок, содержащий определение класса, предшествующее объявлению статической функции, не сможет скомпилироваться из-за ошибки, которую статически заявленная функция друга не определена в ссылочном модуле перевода. И, во-вторых, ссылочный TU может исключить эту ошибку компиляции, определяя статически объявленную функцию, и это определение сможет получить доступ ко всем личным данным класса, которые функция была объявлена ​​другом. Это говорит о том, что функции друзей всегда должны иметь общедоступную связь, которая является их дефолтом, поскольку это предотвращает это потенциальное нарушение инкапсуляции из-за множественных определений публичной функции связи, являющейся ошибкой компиляции.

Я считаю, что @engf был на правильном пути в своем комментарии к вашему вопросу, вам нужен класс друга, определенный в той же самой трансляции, что и класс, который вы хотите, чтобы иметь доступ. Например.

// A.h

class A
{
public:
   A() : _value(0) {}
private:
   int _value;
   friend struct A_Accessor;
};
// A.cpp

struct A_Accessor
{
   static void IncrementValue(A& a)
   {
      ++a._value;
   }
};


TEST(StaticInit, IncrementA)
{
   A a;
   A_Accessor::IncrementValue(a);
}

Это будет определять IncrementValue таким образом, чтобы он мог получить доступ к личным данным, но не может ссылаться на внешний модуль перевода.