跟进一个相关但相切的问题(How to disambiguate function templates that differ only by return type?),我想问一个与一个函数的返回类型不被视为一部分有关的问题功能的签名。
请考虑以下代码:
#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
的函数类型的 return 类型与 return 函数类型foo
。
但我认为函数的返回类型在函数的签名中不起作用!
根据某种思路,由于函数签名long(&)()
与foo
的签名匹配,因此foo
对此类函数的强制转换应该会成功。
然而,演员阵容没有成功。推理在哪里出错了?如果演员阵容因功能签名而无法失败,那么为什么演员阵容失败?
答案 0 :(得分:8)
你是正确的,返回类型不构成函数的签名。
但是,它确实构成了函数的类型的一部分,并且不允许将指针转换为不兼容的函数类型。
答案 1 :(得分:4)
返回类型是类型的一部分,但不用于重载解析。重要的是不要混淆这些条款。基本上,类型包括参数和返回值,但在重载解析期间,不考虑返回类型。函数或函数指针的类型是调用者和被调用者之间的契约,他们必须完全同意这些条款。
从实际的角度来看,如果您建议允许的话,请考虑会发生什么。想象一下调用约定,其中调用者保留空间并将指向该空间的指针传递给函数,然后函数将在该位置构造返回的对象(这实际上是非常常见的调用约定)。现在考虑一下你被允许执行你提出的演员和以下用例
static_assert(sizeof(T1)<sizeof(T2));
T2 f();
T1 (*p)() = &f;
p(); // call
现在,当编译器正在处理p()
时,它会在某处保留空间,并且根据需要保留sizeof(T1)
的函数类型。然后它调用该函数,该函数最终调用f
,将sizeof(T2)
个字节写入该位置导致溢出。
即使尺寸匹配,代码也会有问题。在T1==int
的平台中考虑T2==float
和sizeof(int)==sizeof(float)
。虽然上面的代码不会导致缓冲区溢出,但存储在返回类型位置的位模式将是float
的位模式,而不是int
的位模式。
答案 2 :(得分:1)
函数的返回类型不是签名的一部分,但签名不是编译器需要知道的,以便正确调用函数指针或引用引用的函数。
除了参数之外,编译器还需要知道返回类型,以便它可以为堆栈上的返回值腾出空间,或者从正确的寄存器或任何调用约定中读取返回值。在给定的实施中提出要求。
这就是为什么返回类型是函数类型的一部分 - 所以类型告诉编译器在编译时需要知道什么才能发出代码来调用函数。
函数签名
long(&)()
匹配foo的签名
long(&)()
不是函数签名,而是类型。 foo(void)
是(表示)函数签名。它包括名称和参数。但是你永远不需要在C ++代码中指定一个函数签名(好吧,也许是传递给dlsym
或类似的字符串)。函数签名的确定表示是给定实现中函数的错位名称。修改方案不是标准的,它取决于实现(尽管如果不同的实现想要调用彼此的库,那么它们必须使用相同的方案,因此操作系统可能会指定一个)。
答案 3 :(得分:0)
函数的返回类型被视为其签名(及其类型)的一部分。但是,如果返回类型是“协变”,则允许将函数指针分配给具有不同返回类型的变量。