qsort()/bsearch() 的外部“C”和“C++”版本的重载解决方案

Posted

技术标签:

【中文标题】qsort()/bsearch() 的外部“C”和“C++”版本的重载解决方案【英文标题】:Overload resolution on extern "C" and "C++" version of qsort()/bsearch() 【发布时间】:2014-03-24 04:46:17 【问题描述】:

在C++中,标准库提供了两个版本的qsort()

extern "C" void qsort(void* base, size_t nmemb, size_t size, int (*compar)(const void*, const void*));
extern "C++" void qsort(void* base, size_t nmemb, size_t size, int (*compar)(const void*, const void*));

bsearch() 差不多。

我的问题是,调用qsort() 时,重载解析是如何工作的?它是否会根据作为最后一个参数传递的函数指针的链接类型(“C”或“C++”)自动链接到适当的函数?还是调用者需要使用某种额外的语法显式指定?

(让我们暂时放下打电话给std::sort 的诱惑......)

【问题讨论】:

我从来没有见过 extern "C++", extern "C" 要求 c++ 编译器使用 C mangling。你在哪里看到 extern "C++" void qsort.... @mpromonet:C++ 标准。 我不明白你的问题,但是也许在你的可执行文件(或库)上运行 nm 会回答你的问题? @mpromonet 这个问题对我来说非常清楚。在处理一个没有按照标准指定的方式实现 C++ 的这一方面的实现时,运行 nm 将没有任何帮助。 【参考方案1】:

int (*compar)(const void*, const void*) 参数对于两个不同的重载具有不同的类型。对于第一个重载,它是一个extern "C" 函数指针参数。对于第二个重载,它是一个 extern "C++" 函数指针参数。您传递给qsort 的任何函数指针都已经有某种链接,这就是用来确定要调用哪个重载的。

引用标准:

7.5 联动规范 [dcl.link]

所有函数类型、具有外部链接的函数名和具有外部链接的变量名都有一个语言链接。 [...] 所有函数类型、函数名和变量名的默认语言链接是 C++ 语言链接。具有不同语言链接的两个函数类型是不同的类型,即使它们在其他方面相同。

事实上,我不认为标准实际上意味着要求两个qsort 重载确实具有不同的链接。与 C 不同,标准库函数的用户提供声明是不允许的;它们之间的相关区别是compar 的类型。它们可以被声明为

extern "C" typedef int (*__compar_fnp_c)(const void *, const void *);
extern "C++" typedef int (*__compar_fnp_cxx)(const void *, const void *);
void qsort(void* base, size_t nmemb, size_t size, __compar_fnp_c compar);
void qsort(void* base, size_t nmemb, size_t size, __compar_fnp_cxx compar);

__compar_fnp_c__compar_fnp_cxx 是不同的类型应该更明显。也就是说,as-if 规则不允许这种实现,因为它会破坏采用指针或引用 qsort 的代码。

请注意,GCC 以及其他一些编译器没有正确实现这一点,并且不将链接视为函数指针类型的一部分。在此类实现中,只有一个版本的 qsort 可用,以防止在重载解决期间发生冲突。

【讨论】:

我不太确定函数指针的链接是否是其类型签名的一部分。说,我不知道如何在函数指针声明中指定链接。模仿您使用的 typedef 语法,我在 g++ 和 clang++ 中都遇到了像 extern "C" int (*p)(int); 这样的声明的编译错误。 @goodbyeera 您在此处使用的确切行已被我的系统上的 g++ 和 clang++ 接受,版本分别为 4.8.2 和 3.4。 (编辑)如果您尝试将其用于局部变量声明,那么您是对的,语法不允许这样做。对于块范围的声明或定义,语法不允许以任何方式指定函数指针的类型。您需要改用文件范围的 typedef,并在本地变量中使用该 typedef。 @goodbyeera 我编辑了我的评论,几乎在你发布新评论的同时解决了这个问题:) 非常感谢您找到标准的相关部分。这很清楚。编译器是否尊重这一点似乎是一个单独的问题。我尝试声明函数采用函数指针参数,该参数仅在其链接上有所不同,并且 g++ 和 clang++ 都报告了函数重新定义。 (当然,我们可能没有像您介绍的那样使用 typedef 魔法以正确的方式声明链接。) @goodbyeera 我猜你可能做对了。 GCC 和 clang 都有关于这个问题的开放错误(GCC PR 2316、clang bug 15563)。对于 GCC,a two-year-old proof-of-concept patch 可用,如果您愿意,可以尝试一下。

以上是关于qsort()/bsearch() 的外部“C”和“C++”版本的重载解决方案的主要内容,如果未能解决你的问题,请参考以下文章

go语言的排序结构体排序

回调函数(callback)是啥? ,,

bsearch的使用

回调函数是啥意思 啥是回调函数

c语言重要库函数解读 和模拟实现————Qsort

C语言篇 + 指针进阶练习 + qsort模拟实现(回调函数思想) + 指针和数组笔试题