我可以合法地将成员函数指针强制转换为函数指针吗? 我继承了一些C++代码,我一直在处理消除警告。

我可以合法地将成员函数指针强制转换为函数指针吗? 我继承了一些C++代码,我一直在处理消除警告。,c++,member-function-pointers,C++,Member Function Pointers,这里我们有一个成员函数指针被转换为函数指针。 我知道成员函数指针与函数指针“不同”,因为在引擎盖下有一个隐式的“this”参数。然而,我的前任似乎明确地利用了这一事实,将成员函数指针强制转换为插入了附加第一个参数的函数指针 我的问题是: A) 我可以消除编译器警告吗 B) 此代码在多大程度上保证有效 为了解决这个问题,我将其缩减为一个小main.cpp: #define GENERIC_FUNC_TYPE void(*)(void) #define FUNC_TYPE int(*

这里我们有一个成员函数指针被转换为函数指针。 我知道成员函数指针与函数指针“不同”,因为在引擎盖下有一个隐式的“this”参数。然而,我的前任似乎明确地利用了这一事实,将成员函数指针强制转换为插入了附加第一个参数的函数指针

我的问题是:


A) 我可以消除编译器警告吗

B) 此代码在多大程度上保证有效

为了解决这个问题,我将其缩减为一个小main.cpp:

#define GENERIC_FUNC_TYPE   void(*)(void)
#define FUNC_TYPE       int(*)(void *)

class MyClass
{
public:
    MyClass(int a) : memberA(a) {}
    int myMemberFunc()
    {
        return memberA;
    }

private:
    int memberA;
};

int main(int argc, char*argv[])
{
    int (MyClass::* memberFunc) () = &MyClass::myMemberFunc;
    MyClass myObject(1);
    std::cout << (myObject.*memberFunc)() << std::endl;
    // All good so far

    // Now get naughty, store it away in a very basic fn ptr
    void(*myStoredFunction)(void) = (GENERIC_FUNC_TYPE)memberFunc;  // Compiler warning

    // Reinterpret the fn pointer as a pointer to fn, with an extra object parameter
    int (*myExtractedFunction)(void*) = (FUNC_TYPE)myStoredFunction;

    // Call it
    std::cout << myExtractedFunction(&myObject) << std::endl;
}
IMHO此代码对编译器的底层机制进行假设。或者这些假设对于所有C++编译器都是有效的,有人能帮助吗?
(在实际代码中,我们在一个映射中按名称存储了一大堆函数指针。这些函数都有不同的签名,这就是为什么它们都转换为同一个签名void(*)(void)。这类似于上面的myStoredFunction。然后在调用时将它们转换为单个签名,类似于上面的myExtractedFunction。)

创建完全避免转换的函数如何:

模板
void AsFunc(void*p)
{
(静态铸造(p)->*M)();
}
然后

void(*myStoredFunction)(void)=&AsFunc;
在C++17中,由于某些特性,您甚至可能拥有
模板

void AsFunc(void*p)
void(*myStoredFunction)(void)=&AsFunc

要回答标题中的问题,不,您不能合法地将指向成员函数的指针强制转换为指向函数的指针。据推测,这就是演员阵容中的“编译器警告”所说的

当遇到格式错误的代码(这有点过于简化)时,一致性编译器需要发出诊断,而这个编译器做到了。它发出了警告。这样,编译器就可以自由地做一些特定于实现的事情,这似乎已经做到了:它将代码编译成了您希望的东西


编译器可以自由地以任何工作方式表示指向成员函数的指针,对于非虚拟函数,这可能只是指向函数的“普通”指针。但是用一个虚拟函数试试看;我敢打赌后果会更为严重。

由于在传递大量因函数不同的参数时,显然需要对某个“非类型化”对象(
void*
)按名称调用函数,因此需要某种类型的多次分派。一种可能的解决办法是:

#包括
#包括
#包括
#包括
#包括
#包括
模板
使用FunctionMap=std::map;
类AbstractBaseSubject{
公众:
虚拟void调用(const std::string&fName,const std::string&arg)=0;
};
模板
类BaseSubject:公共抽象BaseSubject{
公众:
虚拟void调用(常量std::string和fName,常量std::string和arg){
const FunctionMap&m=Class::FunctionMap;
自动iter=m.find(fName);
if(iter==m.end())
抛出std::无效的参数(“未知函数\”+fName+“\”);
iter->second(*静态(本次),arg);
}
};
类别Cat:公共基础科目{
公众:
Cat(const std::string&name):name(name){}
void喵喵叫(const std::string和arg){
std::cout*M)();
}
int main(){
void(*funPtr)(X*)=AsFunc;
X;
funPtr&x;
}
程序正确打印:

B::foo() x=B
如果我们查看
AsFunc
的反汇编,我们会看到:

c90 <void AsFunc<B, &B::foo, X>(X*)>:
 c90:   48 83 c7 01             add    $0x1,%rdi
 c94:   e9 07 ff ff ff          jmpq   ba0 <B::foo()>
c90:
c90:48 83 c7 01添加$0x1,%rdi
c94:e9 07 ff ff jmpq ba0

编译器自动生成代码,将
1
添加到
this
指针,这样
B::foo
this
调用,this
指向
X
B
基类。要在
AsFunc
函数中实现这一点(而不是嵌入
main
),我引入了
Obj
模板参数,该参数允许
p
参数为派生类型
X
,以便
AsFunc
必须进行添加。

A)我可以消除编译器警告吗

是-在静态函数的调用中包装成员函数

(这是@Jarod42基于模板的答案的低技术变体)

B) 此代码在多大程度上保证有效

不是(总结@Pete Becker的答案),除非你摆脱了警告

这是我们所使用的JIST。我们保持它的简单性,以减少代码的中断。我们避免了高级C++特性,以使能处理代码的人的数量最大化。
#include <iostream>

class MyClass
{
public:
    MyClass(int a) : memberA(a) {}
    static int myMemberFuncStatic(MyClass *obj)
    {
        return obj->myMemberFunc();
    }   
    int myMemberFunc()
    {
        return memberA;
    }

private:
    int memberA;
};

typedef void(*GENERIC_FUNC_TYPE)(void);
typedef int(*FUNC_TYPE)(MyClass *);

int main(int argc, char*argv[])
{
    int (* staticFunc) (MyClass *) = &MyClass::myMemberFuncStatic;
    MyClass myObject(1);
    std::cout << staticFunc(&myObject) << std::endl;
    // All good so far

    // This is actually legal, for non-member functions (like static functions)
    GENERIC_FUNC_TYPE myStoredFunction = reinterpret_cast<GENERIC_FUNC_TYPE> (staticFunc);  // No compiler warning

    // Reinterpret the fn pointer as the static function
    int (*myExtractedFunction)(MyClass*) = (FUNC_TYPE)myStoredFunction;

    // Call it
    std::cout << myExtractedFunction(&myObject) << std::endl;
}
#包括
类MyClass
{
公众:
MyClass(IntA):memberA(a){}
静态int myMemberFuncStatic(MyClass*obj)
{
返回obj->myMemberFunc();
}   
int myMemberFunc()
{
返回成员A;
}
私人:
国际成员a;
};
typedef void(*通用函数类型)(void);
typedef int(*FUNC_TYPE)(MyClass*);
int main(int argc,char*argv[])
{
int(*staticFunc)(MyClass*)=&MyClass::myMemberFuncStatic;
MyClass myObject(1);

Std::CUT它绝对不是“对所有C++编译器都有效”由于存在成员函数的调用约定,非成员函数无法复制这些约定。在我看来,如果有这种双向映射,就不需要将它们存储在同构列表中。我真的要退一步,重新思考一下您试图实现的目标(与您当前认为需要的实现完全解耦)。不要对类型别名使用
#define
。使用
typedef
使用
,例如
使用GENERIC\u FUNC\u type=void(*)(void);
,这就是它们都是c的原因
B::foo() x=B
c90 <void AsFunc<B, &B::foo, X>(X*)>:
 c90:   48 83 c7 01             add    $0x1,%rdi
 c94:   e9 07 ff ff ff          jmpq   ba0 <B::foo()>
#include <iostream>

class MyClass
{
public:
    MyClass(int a) : memberA(a) {}
    static int myMemberFuncStatic(MyClass *obj)
    {
        return obj->myMemberFunc();
    }   
    int myMemberFunc()
    {
        return memberA;
    }

private:
    int memberA;
};

typedef void(*GENERIC_FUNC_TYPE)(void);
typedef int(*FUNC_TYPE)(MyClass *);

int main(int argc, char*argv[])
{
    int (* staticFunc) (MyClass *) = &MyClass::myMemberFuncStatic;
    MyClass myObject(1);
    std::cout << staticFunc(&myObject) << std::endl;
    // All good so far

    // This is actually legal, for non-member functions (like static functions)
    GENERIC_FUNC_TYPE myStoredFunction = reinterpret_cast<GENERIC_FUNC_TYPE> (staticFunc);  // No compiler warning

    // Reinterpret the fn pointer as the static function
    int (*myExtractedFunction)(MyClass*) = (FUNC_TYPE)myStoredFunction;

    // Call it
    std::cout << myExtractedFunction(&myObject) << std::endl;
}