1

可能重复:
返回类型是函数签名的一部分吗?

跟进一个相关但切题的问题(如何消除仅因返回类型不同而不同的函数模板?),我想问一个与函数的返回类型不被认为是签名的一部分有关的问题的一个功能。

考虑以下代码:

#include <iostream>

int foo()
{
    return 0;
}

int main()
{
    long n = static_cast<long(&)()>(foo)(); // Error: incorrect return type
    int p = static_cast<int(&)()>(foo)(); // Compiles just fine 
}

上面提到的代码行会导致编译错误,因为要转换为的函数类型的返回类型与函数的返回类型foo不匹配。foo

但是我认为函数的返回类型在函数的签名中不起作用!

按照某种思路,既然函数签名long(&)()与 的签名匹配foo,那么foo对这种类型的函数的强制转换应该会成功。

然而,演员没有成功。推理哪里出错了?如果由于函数签名而强制转换不能失败,那么为什么强制转换失败?

4

4 回答 4

8

您是正确的,返回类型不构成函数签名的一部分。

但是,它确实构成函数类型的一部分,并且不允许将指针转换为不兼容的函数类型。

于 2012-12-03T14:45:30.610 回答
4

返回类型类型的一部分,尽管不用于重载决议。重要的是不要混淆这些术语。基本上,类型包括参数和返回值,但在重载决策期间,不考虑返回类型。函数或函数指针的类型是调用者和被调用者之间的约定,他们必须完全同意条款。

从实际的角度来看,考虑如果您的建议被允许会发生什么。想象一个调用约定,其中调用者保留空间并将指向该空间的指针传递给函数,然后函数将在该位置构造返回的对象(这实际上是一种非常常见的调用约定)。现在考虑您被允许执行您建议的演员表和以下用例

static_assert(sizeof(T1)<sizeof(T2));
T2 f();
T1 (*p)() = &f;

p();                                  // call

现在,当编译器处理p()它时,它会在某处保留空间,并给出它需要保留的函数类型sizeof(T1)。然后它调用该函数,该函数最终调用fsizeof(T2)字节写入导致溢出的位置。

即使尺寸匹配,代码也会有问题。考虑T1==intT2==float在一个平台上sizeof(int)==sizeof(float)。虽然上面的代码不会导致缓冲区溢出,但存储在返回类型位置的位模式将是 a 的float,而不是 an 的int

于 2012-12-03T14:52:05.607 回答
1

函数的返回类型不是签名的一部分,但签名不是编译器为了正确调用函数指针或引用所引用的函数而需要知道的。

除了参数之外,编译器还需要知道返回类型,以便它可以为堆栈上的返回值腾出空间,或者从正确的寄存器中读取返回值,或者调用约定在给定的实施。

这就是为什么返回类型是函数类型的一部分——因此类型告诉编译器它在编译时需要知道什么,以便发出代码来调用函数。

函数签名long(&)()与 foo 的签名匹配

long(&)()不是函数签名,它是typefoo(void)是一个(表示一个)函数签名。它包括名称和参数。但是您永远不需要在 C++ 代码中指定函数签名(嗯,可能作为传递给dlsym或类似的字符串)。函数签名的最终表示是给定实现中函数的错位名称。修改方案不是标准的,它取决于实现(尽管如果不同的实现想要调用彼此的库,那么它们必须使用相同的方案,因此操作系统可能会指定一个)。

于 2012-12-03T16:32:29.843 回答
0

函数的返回类型认为是其签名(及其类型)的一部分。但是,如果返回类型是“协变的”,则允许将函数指针分配给具有不同返回类型的变量。

于 2012-12-03T14:33:41.947 回答