![](/img/trans.png)
[英]vc++ stdcall function assign to cdecl function pointer worked, why?
[英]Confusion with function pointer, __cdecl, and template
在 Visual Studio 2019 中,我编写了以下测试代码,但结果让我很困惑。
#include <iostream>
using namespace std;
template<class T, class Func>
int call(T x, Func f) { return f(x); }
int square(int x) { return x * x; }
int main() {
int (*func0) (int) = square; // line 0, OK
//int (func1)(int) = square; // line 1, wrong
int (__cdecl *func1) (int) = square; // line 2, OK
//int (__cdecl func2)(int) = square; // line 3, wrong
cout << ((int(__cdecl*)(int)) square)(5) << endl; // line 4, OK
//cout << ((int(__cdecl)(int)) square)(5) << endl; // line 5, wrong
cout << call<int, int (*)(int)>(5, square) << endl; // line 6, OK
//cout << call<int, int ()(int)>(5, square) << endl; // line 7, wrong
cout << call<int, int(__cdecl*)(int)>(5, square) << endl; // line 8, OK
cout << call<int, int(__cdecl)(int)>(5, square) << endl; // line 9, OK
return 0;
}
(我知道我可以在使用call
时省略类型,但这是一个实验。)
我以为我能够理解从第 0 行到第 7 行的所有内容。我想到的是square
是一个函数指针,因此它应该具有int (*) (int)
或int(__cdecl*) (int)
类型, 并且这两个要么相同,要么可以相互转换(我没有更改项目的调用约定,因此默认为__cdecl
)。
然而,令我惊讶的是第 8 行和第 9 行都能正确编译和运行。 为什么会这样?
通过将第 6、7 行与第 8、9 行进行比较,我认为问题来自添加__cdecl
,但在Microsoft Docs中没有提到这样的内容。
然后我打印出类型:
// ...
cout << typeid(square).name() << endl; // output: int __cdecl(int)
cout << typeid(*square).name() << endl; // output: int __cdecl(int)
cout << typeid(&square).name() << endl; // output: int(__cdecl*)(int)
cout << (typeid(square) == typeid(int(*) (int))) << endl; // output: false
cout << (typeid(square) == typeid(int(__cdecl) (int))) << endl; // output: true
cout << (typeid(square) == typeid(int(__cdecl*) (int))) << endl; // output: false
cout << (typeid(square) == typeid(*square)) << endl; // output: true
// ...
似乎square
确实具有int (__cdecl) (int)
类型。 另外,我不明白为什么square
和*square
属于同一类型......
有人可以向我解释这些现象吗?
square
和*square
是相同的类型,因为函数会衰减,就像 arrays 一样,指向指针,除了(就像数组一样)在某些情况下。 特别是,衰减在typeid
和&
下被抑制,但不是在*
下,因此typeid(square)
为您提供square
类型int (__cdecl)(int)
,而typeid(*square)
表示typeid(*&square)
表示typeid(square)
给出同样的东西。 这导致了一个奇怪的事实,即您可以编写任意数量的*
s,但它们都不会做任何事情: *************square
与square
相同。
现在,对于您问题的 rest,您将类型“函数采用int
返回int
”写错了。 int ()(int)
表示“没有 arguments 返回 function 的函数采用int
返回int
”。 你想要int(int)
。 然后这个工作:
cout << call<int, int(int)>(5, square) << "\n"; // line 7, fixed (FYI: endl is not normally necessary)
因为现在call
有 arguments list (int x, int f(int))
,并且 function 类型的参数声明被自动调整为具有指向 function 类型的指针,在功能上与call<int, int (*)(int)>
call<int, int(int)>
类型相同, call<int, int (*)(int)>
. (这不适用于变量声明或强制转换,因此第 1、3、5 行仍然不正确。)额外的括号导致类型被误解。 第 9 行有效,因为将__cdecl
放在括号内使它们不会被误解(而不是 function 声明符,它们成为分组符号)。
cout << call<int, int (__cdecl)(int)>(5, square) << "\n"; // line 9, OK
再次调整call
参数的类型。 int (__cdecl f)(int)
变为int (__cdecl *f)(int)
,这使得第 9 行在功能上与第 8 行相同。
该行的错误:
int (func1)(int) = square; // line 1, wrong
是你错过了一个'*',它需要是:
int (*func1)(int) = square; // line 1, wrong
与
//int (__cdecl func2)(int) = square; // line 3, wrong
cout << ((int(__cdecl)(int)) square)(5) << endl; // line 5, wrong
需要是
cout << ((int(__cdecl*)(int)) square)(5) << endl; // line 5, wrong
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.