为什么C ++ 11不支持在静态成员函数上声明extern“C”?

前提是我有一个包含声明为void g(void (*callback)());的函数的C库void g(void (*callback)()); 以下代码优雅但非法:

 struct A { // error C2159: more than one storage class specified (VC++ Nov 2012 CTP) static extern "C" void callback() {} }; g(A::callback); 

为什么C ++ 11不支持这个?

这是一个特别令人困惑的话题。 让我们攻击§7.5“链接规范”[dcl.link]。

1)所有函数类型,具有外部链接的函数名称和具有外部链接的变量名称具有语言链接

请注意,语言链接的属性适用于两种完全不同的实体: 类型名称

函数在其类型中具有通常不可见的信息位,用于标识它符合的ABI:C调用约定,Pascal,Fortran,可以指定以不同方式使用堆栈,因此通过指针调用它们需要知道隐形语言标签。

来自另一种语言的变量或函数的名称可以通过C ++语法访问,或者从引用C ++声明的其他语言访问。 但并非每种语言都能与C ++的命名方案和OO模型相匹配。 因此,此方案中的接口不包括类。

因为这些东西是分开管理的,所以它的类型(调用约定)和它的名称(链接符号)可能有不同的链接。

4)链接规范嵌套。 当链接规范嵌套时,最里面的规则确定语言链接。 链接规范没有建立范围。 链接规范只应在命名空间范围内发生(3.3)。 在链接规范中 ,指定的语言链接适用于所有函数声明符的函数类型,具有外部链接的函数名称,以及在链接规范中声明的具有外部链接的变量名称 在确定类成员名称和类成员函数的函数类型的语言链接时,忽略AC语言链接。

extern "C" {}影响所有函数声明,包括指针和引用,但成员函数除外。 由于函数只能在命名空间或成员中定义,因此只能在命名空间范围内定义C函数。

该标准在此给出了一个例子:

 extern "C" typedef void FUNC_c(); class C { // the name of the function mf1 and the member // function's type have C++ language linkage; the // parameter has type pointer to C function void mf1(FUNC_c*); // the name of the function mf2 and the member // function's type have C++ language linkage FUNC_c mf2; // the name of the data member q has C++ language // linkage and the data member's type is pointer to // C function static FUNC_c* q; }; 

但是,您可以使用typedef模拟所需的行为。 从§7.5/ 4中的另一个例子来看,

 extern "C" typedef void FUNC(); // the name f2 has C++ language linkage and the // function's type has C language linkage FUNC f2; 

将这些示例与您的示例相结合,您就可以拥有

 extern "C" typedef void callback_t(); callback_t A_callback; // declare function with C++ name and C type struct A { static callback_t &callback; // not a member function }; // in source file: // definition matches semantics of declaration, although not syntax void A_callback() { ... } // define static member reference callback_t &A::callback = A_callback; g(A::callback); // call syntax is emulated 

在实践中,它很少有所作为。 C和C ++在大多数平台上使用兼容的调用约定(请参阅Jonathan Wakely在此页面上对exception的评论),只要您不尝试传递或返回非POD C ++类类型即可。 这是一个较少实现的C ++特性,由于术语的过载和从微妙到学术的概念区别混淆。

首先,function声明合法的。 但是,对于类成员, extern "C"被忽略,因此如果g期望extern "C" void (*)() ,则不能传递callback

至于为什么这样,我怀疑最初,它主要是一个正交性的问题:一般来说,类成员函数是extern "C"和正交性(或者根本没有人考虑静态的情况)是没有意义的成员)意味着这也适用于静态成员函数,虽然允许它们是extern "C"将是有用的。 今天(即C ++ 11),更改规则会有问题,因为它可能会破坏现有代码。 恕我直言,这种改变是可以接受的,因为它破坏的代码量可能非常小,破坏导致编译时错误 – 而不是运行时语义的改变 – 所以很容易检测和修复。 尽管如此,据我所知,没有人提出改变这一点的建议,所以它没有改变。