8

当调用像 copy_if、transform 等将一元或二元函数作为最后一个参数的 C++ 算法时,我可以传递像 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));

这种用法是否保证适用于所有 C++ 编译器?

用 C++ 思考的书提到“这适用于某些编译器,但不是必需的”。提到的原因是(据我了解)transform 是 C++ 函数,并期望它的最后一个参数具有相同的调用约定。

该书还建议了解决此问题的方法,即在单独的 cpp 文件中创建这样的包装函数,并且不包含 iostreams 头文件。

// tolower_wrapper.cpp
string strTolower(string s) {
  transform(s.begin(), s.end(), s.begin(), tolower);
  return s;
} 

这工作正常,但我不明白这如何解决调用约定问题?transform 仍然是 c++ 函数,tolower 仍然是 strTolower 中的 C 函数,所以这里如何处理不同的调用约定。

4

1 回答 1

2

首先要注意的是算法可以将函数指针或函数对象作为参数,这实际上不是您问题的一部分,但可能有助于解释阅读本文的人。

函数指针就是这样 - 指向一个函数的指针,该函数期望采用一组特定的参数并返回特定的类型。

函数对象是具有重写 operator() 的类的实例。

扩展算法模板时,编译器将能够查看两种情况中的哪一种适用,并生成适当的调用代码。

在将 C 函数用作算法中的二进制函数的情况下,它是您提供的函数指针。您可以从 C++ 调用 C 函数,只要它被声明为extern C { ... }.

许多编译器都带有 C 库函数的头文件,其中包括如下内容:

#ifdef  __cplusplus
extern "C" {
#endif

/* function declarations here */

#ifdef  __cplusplus
}
#endif

因此,如果您包含来自 C++ 程序的 C 库头文件,则包含的函数都将神奇地可供您使用。但是,标准不保证该部分,这就是为什么您的书指出它可能不适用于所有编译器的原因。

另一个问题是,您不允许将函数指针强制转换为具有不同语言链接的类型,至少在您正在执行的一些示例中,尽管有些编译器似乎确实允许这样做 - 例如参见这个GCC Bug

另一个特别适用tolower于例如的问题是,C 库函数的某些名称也是 C++ 标准库中的函数或模板的名称。例如,名称 tolower 也定义在<locale>. 此GCC 错误报告中讨论了此特定情况。使用在不包含冲突声明的单独编译单元中编译的包装器将解决此问题。

于 2014-03-18T01:13:31.197 回答