【问题标题】:Confusion with function pointer, __cdecl, and template与函数指针、__cdecl 和模板混淆
【发布时间】:2020-10-07 19:57:31
【问题描述】:

在 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】:

    square*square 是同一类型,因为函数会像数组一样衰减到指针,但在某些上下文中(就像数组一样)除外。特别是,在typeid&amp; 下抑制衰减,但在* 下没有,所以typeid(square) 给你squareint (__cdecl)(int) 的类型,而typeid(*square) 表示typeid(*&amp;square) 表示@987654331 @ 给出了同样的东西。这导致了一个奇怪的事实,即您可以编写任意数量的*s,但它们都不会执行任何操作:*************squaresquare 相同。

    现在,对于剩下的问题,您写的类型“函数采用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&lt;int, int(int)&gt; 在功能上与call&lt;int, int (*)(int)&gt; 相同。 (这不适用于变量声明或强制转换,因此第 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 行相同。

    【讨论】:

    • 我明白了,这是我不知道的衰变。现在我明白了这里的所有逻辑。但是*************square == square 仍然是一件奇怪的事情......感谢您的回答。
    【解决方案2】:

    该行的错误:

    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
    

    【讨论】:

    • 是的,这是我理解的语法。我故意把这些错误的行放在一起比较。我的主要问题是,为什么第 9 行可以?
    猜你喜欢
    • 1970-01-01
    • 2017-07-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多