下面通过指向成员函数调用D::foo
函数的方法将产生错误:必须使用.*
或->*
来调用指针到成员函数' f(...)'
..当然,这不是我们如何调用指向成员的函数。
正确的调用方式是(d.*f)(5);
或(p->*f)(5);
我的问题是,'有没有办法在没有左侧的类对象的情况下调用类的成员函数?我想知道我们是否可以将类对象(this
)作为常规参数传递?
在我看来,在一天结束时(在汇编/二进制级别),类的所有成员函数都是普通函数,它们应该在n + 1个参数上运行,其中(+ 1代表this
)
如果我们在下面讨论D::foo
函数,那么在汇编/二进制级别它应该运行两个参数:
this
的D类对象的指针)int
。那么,有没有办法(或者黑客)调用D::foo
将类对象作为函数参数传递给它而不是在类对象上使用. or -> or .* or ->*
运算符?
示例代码:
#include <iostream>
using namespace std;
class D {
public:
void foo ( int a ) {
cout << "D" << endl;
}
int data;
};
//typedef void __cdecl ( D::* Func)(int);
typedef void ( D::* Func)(int);
int main ( void )
{
D d;
Func f = &D::foo;
f(&d, 5);
return 1;
}
一种方法是使用增强绑定,即
(boost:: bind (&D::foo, &d, 5)) ();
编辑: “请注意,我不是在寻找一个有效的程序版本,我知道如何使它工作”
答案 0 :(得分:19)
您真的想在不使用.
或->
的情况下调用成员函数吗?真的,真的?好吧,好吧......
Evil.h:
#ifdef __cplusplus
extern "C" {
#endif
struct MyStruct
{
#ifdef __cplusplus
MyStruct();
void method(int);
#endif
};
#ifdef __cplusplus
}
#endif
Evil.cc:
#include <iostream>
#include "evil.h"
MyStruct::MyStruct() { std::cout << "This is MyStruct's constructor" << std::endl; }
void MyStruct::method(int i) { std::cout << "You passed " << i << std::endl; }
Evil.c:
#include "evil.h"
int main()
{
struct MyStruct my_struct;
_ZN8MyStructC1Ev(&my_struct); /* MyStruct::MyStruct() */
_ZN8MyStruct6methodEi(&my_struct, 3); /* MyStruct::method(int) */
return 0;
}
这适用于我在Linux上使用gcc和g ++的组合,但不用说它依赖于平台ABI,并且在调用下划线 - 大写字母形式的函数时违反了C89标准。它几乎肯定不适用于虚函数,我不倾向于尝试。它也可能是我写过的最邪恶的东西。但还是......
编辑:引用OP:
在我看来,在一天结束时(在汇编/二进制级别),类的所有成员函数都是普通函数,它们应该在n + 1个参数上运行,其中(+ 1代表
this
)
虽然自从CFront以来每个编译器都是这样做的,但这只是一个实现细节。 C ++标准很难而不是来指定成员函数应该如何实现,以及它们应该如何表现。
因为它是一个实现细节,不同的平台以不同的方式完成它。这不仅仅是名称错误。例如,Linux上使用的调用约定指定this
作为第一个参数传递;其他实现(Borland,IIRC?)将this
作为 last 参数传递。
因此,如果您希望将成员函数视为具有额外this
的普通函数,则必须将自己限制为特定的ABI。这篇文章提供了一个如何做到这一点的例子(或者更确切地说,为什么你真的不应该这样做的一个例子!)
所以,有没有办法(或者hack)调用D :: foo,并将类对象作为函数参数传递给它而不是使用。或 - &gt;或。*或 - &gt; *类对象上的运算符?
特定于平台,令人厌恶的肮脏黑客......
答案 1 :(得分:14)
我不太确定你在问什么。通过指向成员的指针调用函数没有“人为限制”;你只需要使用正确的语法:
(d.*f)(5); // for objects or references
(p->*f)(5); // for pointers
bind
没有做任何“魔法”;在其实施的某个地方,它确实如此。
在一天结束时它是一个带有两个参数的函数
不,它是一个成员函数,它接受一个参数,并在一个对象上调用。虽然在概念上类似于带有两个参数的函数,但有一个很大的区别:成员函数可以是虚拟的,涉及运行时调度机制。
答案 2 :(得分:4)
我看到的唯一方法是用行为类似函数的类替换你的typedef-ed函数指针。
你正在寻找这样的东西吗?在下面的代码中,我使用一个宏来键入一个模板,然后像你描述的那样操作。
#include <iostream>
using namespace std;
class D {
public:
void foo ( int a ) {
cout << "D" << endl;
}
int data;
};
template<class Object, class Param, class Function>
class FunctionPointerHelper
{
private:
Function m_function;
public:
FunctionPointerHelper(Function function) :
m_function(function)
{
}
void operator=(Function function)
{
m_function = function;
}
void operator()(Object& object, Param param) const
{
(object.*m_function)(param);
}
};
#define TYPEDEF_NICE_FUNCTION(RESULT, CLASS, NEW_TYPENAME, PARAM) \
typedef RESULT ( CLASS::* NEW_TYPENAME_INTERMEDIATE)(PARAM) ; \
typedef FunctionPointerHelper<CLASS, PARAM, NEW_TYPENAME_INTERMEDIATE> NEW_TYPENAME;
TYPEDEF_NICE_FUNCTION(void, D, Func, int)
int main ( void )
{
D d;
Func f = &D::foo;
f(d, 5);
return 1;
}
答案 3 :(得分:3)
有没有办法在没有左侧的类对象的情况下调用类的成员函数?
没有汇编的两种方式:
#include <iostream>
using namespace std;
class D {
public:
void foo ( int a ) {
cout << "D" << endl;
}
static void foo(D& d, int a)
{
d.foo(a);
}
int data;
};
void foo(D& d, int a)
{
d.foo(a);
}
int main ( void )
{
D d;
D::foo(d, 5);
foo(d, 5);
return 0;
}
答案 4 :(得分:2)
但必须有一种方法来避免这种人为限制 由c ++强加的
'人工限制'是什么意思?这只是语言定义的语法。它出什么问题了? bind()
的“魔力”将在内部使用->*
运算符来调用函数。
答案 5 :(得分:2)
是的,C ++ 11标准中有一个名为 INVOKE 的抽象,它适用于Callable对象。指向成员函数(PTMF)对象是可调用的,this
作为第一个(常规)参数传递。
虽然已经提出,但没有std::invoke
功能。要从任何Callable对象获取仿函数,可以使用std::bind
,这是Boost.bind的派生。
这很好用:
int main ( void )
{
D d;
auto f = std::bind( &D::foo, _1, _2 );
f(&d, 5);
}
答案 6 :(得分:1)
是的,确实有足够的扭曲你可以逃避“新”成员函数语法,但我不得不问:你为什么这样做?语法促进了对成员函数以某种方式从周围对象调用的理解。鉴于虚函数的存在,这实际上(并且必须是)情况。它还自动化了this指针,改变了对虚函数的调用(以及对虚拟继承的支持)所需。
有关如何执行此操作的说明,请查看FastDelegate库的实现。它的实现需要知道编译器的成员函数指针的二进制结构(其中可以有多种变体)。这就是你正在寻找的“黑客”。 FastDelegate的委托(即闭包)在调用点变成两条指令:将计算出的“this”值拉到适当的位置(基于调用约定)并间接跳转到实际函数的入口地址。
这最终看起来像这样:
fastdelegate::FastDelegate0<> functionObject;
SomeClass someInstance;
//fill in object and function information in function object
functionObject.bind(&someInstance,&SomeClass::someFunction);
//call function via object. Equivalent to: someInstance->someFunction();
functionObject();
这与boost :: bind和朋友们正在做的非常相似,但通常更快(但显然,便携性较差)。
在这里使用的模板和运算符重载下面有一些数学(在绑定函数内),它计算出如何将some&amp; someInstance改变为SomeClass :: someInstance所需的this指针。它还可以找到基础函数的实际地址,并记录这两个值以供日后使用。当调用发生时,它强制编译器使用->*
通过一些技巧“做正确的事”。但是,如果你真的想避免甚至依赖->*
运算符,那么就可以做一些类型转换并将指针转换为“__thiscall”函数指针(好吧,假设你在Windows上):
__ thiscall调用约定用于成员函数,而且是 这样做的C ++成员函数使用的默认调用约定 不要使用变量参数。在__thiscall下,被调用者清理 堆栈,这对vararg函数来说是不可能的。争论被推了 在堆栈中从右到左,这个指针被传递 通过寄存器ECX,而不是堆栈,在x86架构上。
那么,你究竟不喜欢什么:someInstance-&gt; someFunction()?
答案 7 :(得分:0)
有没有办法在没有左侧的类对象的情况下调用类的成员函数?
...
那么,有没有办法(或者黑客)调用
D::foo
将类对象作为函数参数传递给它,而不是使用.
或->
或.*
或者类对象上的->*
运算符?
简而言之,没有。如果不以某种方式指定调用左侧的对象,则无法调用非static
类成员函数。如果未指定类(用于静态函数)或对象(用于非静态函数),则编译器无法知道您尝试调用哪个函数。它在当前范围之外,因为它在一个类或对象中。有一些方法可以“破解”代码,因此您可以在示例中将其编写为main()
。其他一些答案给出了这些黑客的例子。
------------使用静态函数-----------------
可以在类外部调用类中的函数,而无需指定类对象。这样做的方法是为函数指针创建一个typedef
,其签名与类函数匹配,然后创建这样的指针并为其指定类函数的地址。然后可以调用该函数,而无需在左侧指定类或对象。
功能必须为static
。 ISO C ++不允许使用绑定成员函数的地址来形成指向该函数的指针,即您无法从类对象创建指向非static
成员函数的指针。您可能能够找到一些非ISO标准兼容的编译器,但我没有为您提供任何指导。
参数this
到非static
类成员函数是隐含参数,并且始终是隐含参数。您无法手动指定它,但可以通过将指针传递给类的对象来模拟它。
因此,对于您的代码示例:
void D::foo(int)
应为static
并添加D*
参数:class D { static void D::foo(D*, int); };
。typedef void (D::* Func)(int);
以适应之前的更改并移除D::
:typedef void (*Func)(D*, int);
答案 8 :(得分:0)
该标准非常清晰,完全明确。第5.2.2节(C ++ 11)第一句规定:
有两种函数调用:普通函数调用和成员函数调用。
就是这样 - 它们是不同的东西,你不能混淆它们。
答案 9 :(得分:0)
这是您的程序的一个版本。类方法的指针需要一些复杂的调用,如下所示:
#include <iostream>
using namespace std;
class D {
public:
void foo ( int a ) {
cout << "D" << endl;
}
int data;
};
typedef void ( D::* Func)(int);
int main ( void )
{
D d;
D *pThis = &d;
Func f = &D::foo;
/* invoke function pointer via a "class" pointer */
(pThis->*f)(5);
/* invoke function pointer via a "class" object */
(d.*f)(5);
return 1;
}
此程序的输出如下所示:
~/prj/stackoverflow
# g++ funcptr.cpp
~/prj/stackoverflow
# ./a.out
D
D
编译器通过类方法指针提供方法调用的“this”指针。 “this”指针将是调用对象。
当然有办法。使foo成为一个静态类方法并更改foo的签名以获取“this”指针。
自:
void foo(int a) {}
要:
static void foo(D *pthis, int a) {}
拨打foo w / o a。或 - &gt;,以下将起作用
int main(void)
{
D d;
int a = 0;
D::foo(&d, a); /* call foo, w/o using . or -> and providing your own this pointer */
return 0;
}
答案 10 :(得分:0)
虽然我觉得它有点奇怪,但call_member看起来就像你想要的那样。 (需要C ++ 11)
#include <iostream>
template<typename T, typename Ret, typename... Args>
Ret call_member(Ret (T::*mem)(Args...), T* obj, Args... args)
{
return (obj->*mem)(args...);
}
struct S {
void foo(int i) { std::cout << "Foo: " << i << '\n'; }
};
int main()
{
S s;
call_member(&S::foo, &s, 5);
return 0;
}