Можно ли объявить функцию друга статичной?
Вот пример кода кода С++, который компилируется и работает нормально:
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?
Ответы
Ответ 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 таким образом, чтобы он мог получить доступ к личным данным, но не может ссылаться на внешний модуль перевода.