C / C ++链接约定

时间:2014-03-18 00:21:55

标签: c++ c declspec

当调用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));

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

用C ++提到的这本书提到了#34;这适用于一些编译器,但它并不是必需的。&#34;提到的原因是(据我所知),转换是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函数,所以这里处理这些不同的调用约定。

1 个答案:

答案 0 :(得分:2)

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

函数指针就是这样 - 指向函数的指针,该函数需要获取一组特定参数并返回特定类型。

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

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

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

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

#ifdef  __cplusplus
extern "C" {
#endif

/* function declarations here */

#ifdef  __cplusplus
}
#endif

因此,如果您从C ++程序中包含C库头,那么所包含的函数将完全可供您使用。但是,标准并不保证这一部分,这就是为什么你的书说它可能不适用于所有编译器。

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

另一个特别适用于tolower的catch是C库函数的某些名称也是C ++ std库中函数或模板的名称。例如,名称tolower也在<locale>中定义。这个具体案例在本GCC bug report中讨论。在不包含冲突声明的单独编译单元中编译的包装器的使用将解决此问题。