想象一下以下情况,我有一个noexcept
C++ 自由函数,它作为函数指针传递给另一个具有 C 链接的函数:
extern "C" {
void some_fun(void(*)());
}
void my_fun() noexcept { /* do stuff */; }
int main()
{
some_fun(my_fun);
}
noexcept
这是正确的和/或允许的吗?在这种情况下会发生什么?考虑到 C++17属于函数类型,这里的语义是什么?
此外,令我惊讶的是,这甚至可以编译(使用 gcc 和 clang):
extern "C" {
void some_fun(void(*)() noexcept);
}
void my_fun() noexcept { /* do stuff */; }
int main()
{
some_fun(my_fun);
}
noexcept
C 函数签名中的函数指针允许在其中使用限定符,这是否有意义?
标准 C++ 不允许这样做。另请参阅:`extern "C"` 是否属于函数类型的一部分?,
std::qsort
。参数 tosome_fun
被声明为“指向 C 函数的指针”,并且没有从“指向 C++ 函数的指针”到“指向 C 函数的指针”的标准转换。C 函数可以是
noexcept
。例如,extern "C" void my_fun() noexcept { /* do stuff */; }
是一个 C 函数,你可以将指向它的指针传递给some_fun
。(有一个隐式函数转换可以删除noexcept
)GCC、Clang 和 MSVC 至少不区分 C 和 C++ 函数,甚至不将其记录为类型的一部分,从而允许其进行编译。
我发现了一个编译器不接受这个的例子:如何声明一个外部“C”函数指针
这是正确的,也是允许的。实际发生的情况是,存在一个从
void(*)() noexcept
到 的隐式转换序列void(*)()
(但不是反过来)。此转换会将函数指针转换为不带 的预期类型noexcept
。可以通过指针调用该函数,没有noexcept
任何限制。不要求在带有
"C"
链接的函数声明中使用的类型必须是 C 类型。从 C++ 的角度来看,这与正常行为没有区别,只是函数名称会忽略命名空间并且无法重载(即从典型的实现角度来看,名称修改是禁用的)。但是,这种声明对于与 C 代码的实际互操作意味着什么,是实现定义的。当然,您无法在 C 中声明或定义具有相同签名的函数。如果您更改了程序的 C 部分的签名,您应该真正确定编译器编译代码的具体方式,以确保存在明确定义的行为。
正如@Artyer的另一个回答指出的那样,根据标准,函数的语言链接是其类型的一部分,并且使用
"C"
链接声明的函数中的函数参数继承了此语言链接。my_fun
不是具有"C"
链接的函数,因此不能作为指向具有"C"
链接的函数的指针传递。我认为这是为了允许 C 和 C++ 之间使用不同的调用约定。在所有情况下,我都知道编译器会忽略标准的这一部分,并将语言链接视为不是类型的一部分。