38

这是一些可以编译并正常工作的 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++ 中是否有任何方法可以拥有一个声明为静态的(非方法)友元函数?

4

3 回答 3

26

引用 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
于 2013-11-07T20:09:23.473 回答
17

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

class A;
static void IncrementValue(A&);

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

static void IncrementValue(A&) {
    // code here, of course
}
于 2013-11-07T20:04:28.560 回答
17

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

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

我相信@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);
}

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

于 2016-12-02T16:49:22.100 回答