42

Here is some C++ example code that compiles and works fine:

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

What I would like to do, however, is declare IncrementValue() as static, so that it can't be seen or called from another compilation unit:

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

Doing that, however, gives me a compile error:

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&)’

... and changing the friend declaration to match doesn't help:

friend static void IncrementValue(A &);

... as it gives this error:

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

My question is, is there any way in C++ to have a (non-method) friend function that is declared static?

Jeremy Friesner
  • 70,199
  • 15
  • 131
  • 234
  • 1
    "so that it can't be seen or called from another compilation unit". Why would you need that? (I am genuinely interested.) – Paul Draper Nov 07 '13 at 20:01
  • 1
    It would be strange because a friend function is part of the class's API, so in effect you're saying you want the class to have a *different* API in different source files, according to how that static function is implemented in each. – Steve Jessop Nov 07 '13 at 20:03
  • 4
    mind you ,you're bypassing the private/protected mechanism because you've enabled any compilation unit to provide a static version of IncrementValue that now can do anything it wants. – engf-010 Nov 07 '13 at 20:17
  • @PaulDraper I don't *need* it per se, but since the function is diddling with the private state of the class, I'd prefer to have the compiler guarantee that it's only called from within that class's own .cpp file and nowhere else. – Jeremy Friesner Nov 07 '13 at 20:31
  • 5
    @Jeremy Friesner: for that to work realy safely ,you need a class as friend ,that is defined in your source file so it's only visible to you – engf-010 Nov 07 '13 at 20:55
  • 1
    Any class with an internal-linkage friend would violate the ODR if it were defined in more than one translation unit (because [name lookup differed](https://en.cppreference.com/w/cpp/language/definition#One_Definition_Rule) for the friend declaration), so the internal linkage of the friend wouldn’t change anything. – Davis Herring May 11 '19 at 19:59

3 Answers3

30

Quoting 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).

So you need to declare the function as static prior to declaring it as a friend. This can be done by adding the following declarations above the definition of A.

class A;  // forward declaration, required for following declaration
static void IncrementValue(A&); // the friend declaration will retain static linkage
Praetorian
  • 106,671
  • 19
  • 240
  • 328
  • 1
    I think this would be very unwise. See my answer for details. – Neutrino Dec 02 '16 at 16:50
  • 1
    Doesn't seem to help. With GCC 10.3.0, at least, the `friend` declaration after the `static` declaration seems to re-declare it as `extern`, so when I finally define the static friend function, I get `error: was declared 'extern' and later 'static'`. And before you suggest rewriting the heck out of this code...I'm dealing with a big ball of legacy code, like most people with a day job. – ulatekh Sep 24 '21 at 15:20
19

While Praetorian's answer is technically correct in that it answers the question you explicitly asked, I believe it is not a useful answer in that what he proposes is both unsound and it also does not fulfill your stated objective of wishing to define a method that can be called in the friend classes' translation unit only.

There are two problems with his solution. Firstly any other translation unit that includes the header containing the class definition preceeded by the static function declaration will fail to compile due to the error that the statically declared friend function is not defined in the referencing translation module. And secondly, the referencing translation unit can eliminate that compile error by defining the statically declared function itself, and that definition will be able to access all the private data of the class that the function was declared a friend of. This suggests that friend functions should always be left having the public linkage that is their default, as this prevents this potential encapsulation breach due to multiple definitions of a public linkage function being a compile error.

I believe @engf was on the right track in his comment on your question, you need a friend class defined in the same translation unit as the class you wish it to be able to access. E.g.

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

This will define IncrementValue in a way that permits it to access A's private data, yet cannot be referenced from outside A's translation module.

heretoinfinity
  • 1,528
  • 3
  • 14
  • 33
Neutrino
  • 8,496
  • 4
  • 57
  • 83
17

Sure. Read the second line of the error message carefully: the function was declared extern and later static. So all you have to do is declare it static before the friend declaration:

class A;
static void IncrementValue(A&);

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

static void IncrementValue(A&) {
    // code here, of course
}
Pete Becker
  • 74,985
  • 8
  • 76
  • 165
  • Hi Pete, I tried the recommended approach and it still failing to compile.#include class A; static void frnd(A& a); class A{ public: int k; void print() { std::cout<< "i " << i<< "," <<"j "<< j <<"," <<"k " << k << std::endl; } private: int i; int j; friend static void frnd(A& a); }; static void frnd(A& a) { a.i = 10; a.j = 20; a.k = 30; } int main() { A a; frnd(a); a.print(); } – Mario Super Aug 12 '21 at 16:57