2013-11-07 3 views
19

Вот некоторые C++ пример кода, который компилируется и работает отлично:Можно ли объявить функцию друга статичной?

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 

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

+1

", чтобы его нельзя было увидеть или вызвать из другой единицы компиляции". Зачем вам это нужно? (Я искренне заинтересован.) –

+1

Было бы странно, потому что функция friend является частью API класса, поэтому вы говорите, что хотите, чтобы класс имел * разные * API в разных исходных файлах, в соответствии с тем, как это статическая функция реализуется в каждом. –

+4

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

ответ

16

Цитирование N3691 - §11.3/4 [class.friend]

A function first declared in a friend declaration has external linkage (3.5). Otherwise, the function retains its previous linkage (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 
+0

Я думаю, что это было бы очень неразумно. См. Мой ответ для деталей. – Neutrino

15

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

class A; 
static void IncrementValue(A&); 

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

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

Хотя 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 таким образом, что позволяет ему получить доступ к конфиденциальным данным, и все же не может ссылаться снаружи модуля перевода в.

Смежные вопросы