C++ 为什么C++;11不支持声明extern“;";关于静态成员函数?

C++ 为什么C++;11不支持声明extern“;";关于静态成员函数?,c++,c,static,c++11,extern,C++,C,Static,C++11,Extern,假设我有一个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不支持此功能?首先,函数声明是合法的。外部“C”, 但是

假设我有一个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不支持此功能?

首先,函数声明是合法的。
外部“C”
, 但是,对于类成员,将忽略,因此如果
g
期望
extern“C”void(*)(
),您无法传递它
回调

至于为什么呢,我怀疑原来主要是 一个正交性的问题:这对一个类来说毫无意义 成员函数一般为
外部“C”
,且正交 (或者根本没有人考虑静态构件的情况)意味着 这也适用于静态成员函数,尽管 允许它们成为
extern“C”
会很有用。今天 (即C++11),更改规则会有问题, 因为它可能会破坏现有的代码。嗯,改变会 可以接受,因为它将破坏的代码量是 可能非常小,并且破坏会导致编译时间 错误不是运行时语义的更改,因此很容易 检测并修复。不过,据我所知,没有人能做到
一个改变这个的提议,所以它没有被改变。

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

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

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

一个函数在其类型中通常有一个不可见的信息位,该信息位标识了它所遵循的ABI:C调用约定、Pascal、Fortran,所有这些都可能被指定以不同的方式使用堆栈,因此通过指针调用它们需要知道不可见的语言标记

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

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

4) 链接规格嵌套。当链接规范嵌套时,最里面的规范确定语言 联系。链接规范不确定范围。链接规范只能出现在名称空间范围内(3.3)。在链接规范中,指定的语言链接适用于所有函数声明器的函数类型、具有外部链接的函数名以及在链接规范中声明具有外部链接的变量名。在确定类成员名称和类成员函数的函数类型的语言链接时,忽略C语言链接

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在这个页面上的注释以获得例外),只要您不尝试通过或返回非POD C++类类型。这是一个不太实用的C++特性,因为术语和概念的区别是从细微到学术的混淆。

您没有使用<代码>外部“C”<代码> @ xMLMX吗?如果<代码> g <代码>期望<代码>外部“C”空(*)(/)>代码>,则<代码> g(&::回调)< /C> >是非法的,并且需要编译器诊断。然而,许多编译器并不强制执行这一点(因此在这方面是不正确的),但是C函数不按名称调用回调,它只看到一个指针。因此,名称mangling不是问题。请在问之前先试试。@JoachimPileborg的名字弄脏不是问题。呼叫约定是一个很好的例子。名称混乱是呼叫约定的一部分,但不是全部。实际上我使用了一个编译器,其中C和C++使用不同的调用约定(调用程序在C中被清除,在C++中被调用)。因此无需猜测。
extern“C”
对于类成员是非法的。7.1.1/6:外部说明符只能应用于变量和函数的名称。在类成员或函数参数的声明中不能使用外部说明符。自C++98以来,它也是非法的。@Jon I的陈述基于§7.5/4:“在确定类成员名称和类成员函数的函数类型的语言链接时,忽略了C语言链接。”并且§7.1.1/6没有提到任何链接说明符;§7.1.1完全是关于存储类说明符,而不是链接说明符。@Jon不需要。该标准在很多情况下都不明显。然而,7.5/4还说“链接规范只能出现在名称空间范围内”——这难道不禁止在类声明中使用它吗?@JamesKanze No,
extern“C”
只允许在名称空间范围内使用。7.5/4:“链接规范只能出现在命名空间范围(3.3)”当类出现在
extern“C{}
块中时,同一段落中的引用是指的。我想知道如果让编译器确定