当调用C++算法(如copy_if,transform等,它们将一元或二元函数作为最后一个参数)时,我可以传递一个像atoi或tolower这样的C库函数.
例如,以下调用工作正常并给出正确的输出(在ideone中尝试)
1) transform (foo, foo+5, bar, atoi);
2) transform (foo, foo+5, bar, ptr_fun(atoi));
3) transform(s.begin(),s.end(),s.begin(), static_cast<int (*)(int)>(tolower));
Run Code Online (Sandbox Code Playgroud)
这种用法是否可以保证适用于所有C++编译器?
用C++思考的这本书提到"这适用于一些编译器,但并不是必须的." 提到的原因是(据我所知),转换是C++函数,并期望它的最后一个参数具有相同的调用约定.
本书还提出了解决此问题的方法,即在单独的cpp文件中创建这样的包装函数,并且不包含iostreams头文件.
// tolower_wrapper.cpp
string strTolower(string s) {
transform(s.begin(), s.end(), s.begin(), tolower);
return s;
}
Run Code Online (Sandbox Code Playgroud)
这工作正常,但我不明白这是如何解决调用约定问题的?transform仍然是一个c ++函数,而tolower仍然是strTolower中的一个C函数,所以这里处理这些不同的调用约定.
首先要注意的是,算法可以将函数指针或函数对象作为参数,这实际上不是您问题的一部分,但可能有助于解释阅读本文的人。
函数指针就是这样——指向一个函数的指针,该函数期望接受一组特定的参数并返回特定的类型。
函数对象是重写了operator()的类的实例。
当扩展算法模板时,编译器将能够看到这两种情况适用于哪一种,并生成适当的调用代码。
如果 C 函数在算法中用作二进制函数,则它是您提供的函数指针。您可以从 C++ 调用 C 函数,只要声明它即可extern C { ... }
。
许多编译器都附带了 C 库函数的头文件,其中包括如下内容:
#ifdef __cplusplus
extern "C" {
#endif
/* function declarations here */
#ifdef __cplusplus
}
#endif
Run Code Online (Sandbox Code Playgroud)
因此,如果您在 C++ 程序中包含 C 库头文件,那么所包含的函数将神奇地可供您使用。然而,该部分不受标准保证,这就是为什么您的书指出它可能不适用于所有编译器。
另一个问题是,您不允许将函数指针转换为具有不同语言链接的类型,至少在您正在做的一些示例中,尽管有些编译器似乎确实允许这样做 - 例如,请参阅此 GCC Bug。
另一个问题(特别适用于tolower
例如)是,某些 C 库函数的名称也是 C++ std 库中的函数或模板的名称。例如,名称 tolower 也在 中定义<locale>
。此GCC 错误报告中讨论了这种特定情况。使用在不包含冲突声明的单独编译单元中编译的包装器可以解决此问题。