与函数指针、__cdecl和模板混淆。

问题描述 投票:2回答:1

在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 是同一类型的...

谁能给我解释一下这些现象?

c++ visual-studio templates function-pointers cdecl
1个回答
2
投票

square*square 是相同的类型,因为函数和数组一样,除了(和数组一样)在特定的上下文下,会衰减为指针。特别是,在 typeid&但不是根据 *所以 typeid(square) 给你的类型 square, int (__cdecl)(int)typeid(*square) 途径 typeid(*&square) 途径 typeid(square) 给出了同样的东西。这就导致了一个奇怪的事实,你可以写出尽可能多的 *的,他们都将无所作为。*************square 是相同的 square.

现在,对于你问题的其余部分,你写了 "函数,取 int 回归 int"错误。int ()(int) 意思是 "函数不取参数返回函数取 int 回归 int". 你想 int(int). 那么这就可以了。

cout << call<int, int(int)>(5, square) << "\n"; // line 7, fixed (FYI: endl is not normally necessary)

因为现在 call 有参数表 (int x, int f(int)),并且函数类型的参数声明会自动调整为有指向函数类型的指针,从而使 call<int, int(int)> 在功能上等同于 call<int, int (*)(int)>. (这对变量声明或转储不起作用,所以第1、3、5行仍然不正确。) 额外的括号导致了对类型的误解。第9行是可行的,因为把 __cdecl 内的括号使它们不会被误解(它们不是函数声明符,而是成为分组符号)。

cout << call<int, int (__cdecl)(int)>(5, square) << "\n"; // line 9, OK

同样, call的参数进行调整。int (__cdecl f)(int) 变成 int (__cdecl *f)(int)这使得第9行与第8行功能相同。


0
投票

这一行的错误在于:

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
© www.soinside.com 2019 - 2024. All rights reserved.