具有外部“C”链接的过载分辨率

在混合C / C ++项目中,我们需要从C调用C ++函数。 要调用的函数作为三个单独的函数被重载,但是我们可以忽略C语言中的那个,我们只选择最合适的函数并坚持使用那个函数。

有两种方法可以做到这一点:(1)编写一个带有extern“C”函数的小型C ++包装器,它将调用转发给选定的重载函数,或者(2)只是声明我们想要调用的函数的hackish方式C为外部“C”。

问题是,第二种变体是否有任何缺点(除了恶梦和恶业)? 换句话说,给定三个重载函数,其中一个被声明为exern“C”,我们应该期待C ++方面的问题,还是根据标准很好地定义?

我相信标准中的语言是专门编写的,只允许一个具有“C”链接的函数,以及具有“C ++”链接的任意数量的其他函数,它们重载相同的名称(§[dcl.link] / 6):

最多一个具有特定名称的函数可以具有C语言链接。 具有相同函数名的C语言链接的函数的两个声明(忽略限定它的命名空间名称)出现在不同的命名空间范围内引用相同的函数。 具有相同名称的C语言链接的对象的两个声明(忽略限定它的命名空间名称)出现在不同的命名空间作用域中引用同一个对象。

该标准显示以下示例:

complex sqrt(complex); // C + + linkage by default extern "C" { double sqrt(double); // C linkage } 

只要你遵循extern-C函数的其他规则(例如它们的特殊名称要求),根据标准指定其中一个重载为extern-C就可以了。 如果碰巧使用这些函数的函数指针,请注意语言链接是函数类型的一部分,并且需要指向此函数的函数指针可能会为您决定问题。

否则,我没有看到任何明显的缺点。 即使复制参数和返回值的潜在缺点也可以通过编译器和实现特定来减轻,这些特性允许您内联函数 – 如果确定这是一个问题。

 namespace your_project { // You do use one, right? :) void f(int x); void f(char x); void f(other_overloads x); } extern "C" void f(int x) { your_project::f(x); } 

即使标准允许,代码的未来维护者也可能会非常困惑,甚至可能会删除外部“C”,破坏C代码(可能远远超过事件不可链接)。

只需编写包装器。

编辑:从C ++ 03 7.5 / 5:

如果同一函数或对象的两个声明指定了不同的链接规范(即,这些声明的链接规范指定了不同的字符串文字),如果声明出现在同一个转换单元中,并且一个定义规则,则程序格式错误(3.2)如果声明出现在不同的翻译单元中,则适用

我认为这不适用,因为具有相同名称的C和C ++函数实际上并不是相同的函数,但这种解释可能是错误的。

然后从C ++ 03 7.5 / 6:

最多一个具有特定名称的函数可以具有C语言链接…

这意味着您可以使用具有相同名称的其他非C链接函数。 在这种情况下,C ++重载。

(这个答案适用于C ++ 14;到目前为止的其他答案是C ++ 03)。

允许使用重载。 如果存在某个特定名称的extern "C"函数定义,则以下条件适用(括号中对C ++ 14的引用):

  • extern "C"函数的声明必须在该函数名称的任何声明或重载定义时可见(7.5 / 5)
  • 任何地方都不能有任何其他具有相同名称的函数或变量的extern "C"定义。 (7.5 / 6)
  • 不得在全局范围内声明具有相同名称的重载函数。 (7.5 / 6)
  • 在与extern "C"函数相同的命名空间内,不能有另一个具有相同名称和参数列表的函数声明。 (7.5 / 5)

如果在同一翻译单元中发生任何违反上述规则的行为,编译器必须对其进行诊断; 否则它是未定义的行为,无需诊断。

所以你的头文件可能看起来像:

 namespace foo { extern "C" void bar(); void bar(int); void bar(std::string); } 

最后一个要点是,你不能仅仅因为联系而过载; 这是不正确的:

 namespace foo { extern "C" void bar(); void bar(); // error } 

但是,您可以在不同的命名空间执行此操作:

 extern "C" void bar(); namespace foo { void bar(); } 

在这种情况下,非限定查找的正常规则确定某些代码中的调用bar()是否找到::barfoo::bar不明确