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

xml*_*lmx 15 c c++ static extern c++11

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

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

g(A::callback); 
Run Code Online (Sandbox Code Playgroud)

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

Pot*_*ter 16

这是一个特别令人困惑的话题.让我们攻击§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;
};
Run Code Online (Sandbox Code Playgroud)

但是,您可以使用a模拟所需的行为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;
Run Code Online (Sandbox Code Playgroud)

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

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

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


Jam*_*nze 3

首先,函数声明合法的。extern "C"但是,类成员会忽略 ,因此如果需要gextern "C" void (*)()则无法传递它callback

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

  • @Jon我的声明基于§7.5/4:“在确定类成员名称和类成员函数的函数类型的语言链接时,AC语言链接被忽略。” 并且 §7.1.1/6 没有提及任何有关链接说明符的内容;§7.1.1 完全是关于存储类说明符,而不是链接说明符。 (4认同)
  • `extern "C"` 对于类成员来说是非法的。7.1.1/6: *extern 说明符只能应用于变量和函数的名称。extern 说明符不能用在类成员或函数参数的声明中。* 从 C++98 开始它也是非法的。 (2认同)