是否可以将友元函数声明为静态?

Jer*_*ner 27 c++ static friend

这是一些编译和工作正常的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;
}
Run Code Online (Sandbox Code Playgroud)

但是,我想要做的是将IncrementValue()声明为static,这样就无法从另一个编译单元看到或调用它:

static void IncrementValue(A & a)
{
    a.value++;
}
Run Code Online (Sandbox Code Playgroud)

但是,这样做会给我一个编译错误:

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&)’
Run Code Online (Sandbox Code Playgroud)

...并且更改要匹配的朋友声明无济于事:

friend static void IncrementValue(A &);
Run Code Online (Sandbox Code Playgroud)

...因为它给出了这个错误:

temp.cpp:8: error: storage class specifiers invalid in friend function declarations
Run Code Online (Sandbox Code Playgroud)

我的问题是,在C++中有没有办法让一个声明为静态的(非方法)友元函数?

Pra*_*ian 22

引用N3691 - §11.3/ 4 [class.friend]

首先在朋友声明中声明的函数具有外部链接(3.5).否则,该功能保留其先前的链接(7.1.1).

因此,您需要static 在将其声明为a 之前声明该函数friend.这可以通过在定义之上添加以下声明来完成A.

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

  • 我认为这是非常不明智的。详情请参阅我的回答。 (2认同)

Pet*_*ker 17

当然.仔细阅读错误消息的第二行:该函数已声明extern,稍后 static.所以你要做的就是在朋友声明之前声明它是静态的:

class A;
static void IncrementValue(A&);

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

static void IncrementValue(A&) {
    // code here, of course
}
Run Code Online (Sandbox Code Playgroud)


Neu*_*ino 11

虽然Praetorian的答案在技​​术上是正确的,因为它回答了你明确提出的问题,但我认为这并不是一个有用的答案,因为他提出的建议既不健全,也不符合你所希望定义一种方法的既定目标.仅在朋友班的翻译单元中调用.

他的解决方案有两个问题.首先,由于在引用转换模块中未定义静态声明的友元函数的错误,包含由静态函数声明开头的类定义的头的任何其他转换单元将无法编译.其次,引用TU可以通过定义静态声明的函数本身来消除该编译错误,并且该定义将能够访问该函数被声明为其朋友的类的所有私有数据.这表明友元函数应该始终保留其默认的公共链接,因为这可以防止由于公共链接函数的多个定义是编译错误而导致的潜在封装破坏.

我相信@engf在你对你的问题的评论中走在正确的轨道上,你需要在与你希望它能够访问的类相同的翻译单元中定义的朋友类.例如

// A.h

class A
{
public:
   A() : _value(0) {}
private:
   int _value;
   friend struct A_Accessor;
};
Run Code Online (Sandbox Code Playgroud)
// A.cpp

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


TEST(StaticInit, IncrementA)
{
   A a;
   A_Accessor::IncrementValue(a);
}
Run Code Online (Sandbox Code Playgroud)

这将以允许其访问A的私有数据的方式定义IncrementValue,但不能从外部A的翻译模块引用.