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

时间:2011-02-01 17:44:29

标签: c++ c overloading

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

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

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

4 个答案:

答案 0 :(得分:18)

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

  

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

标准显示以下示例:

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

答案 1 :(得分:2)

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

只需编写包装器。

编辑: 来自C ++ 03 7.5 / 5:

  

如果两个声明相同   函数或对象指定不同   联系规范(即   这些联动规范   声明指定不同   字符串文字),程序是   如果声明出现,则形成不良   在同一个翻译单位,和   一个定义规则(3.2)适用于   声明出现在不同的地方   翻译单位......

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

然后从C ++ 03 7.5 / 6:

  

最多一个具有特定功能的功能   名称可以有C语言链接...

这意味着你可以拥有其他非C-linkage函数,它们具有相同的名称。在这种情况下,C ++重载。

答案 2 :(得分:2)

只要遵循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);
}

答案 3 :(得分:1)

(这个答案适用于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还是不明确