有没有办法在没有 .* 或 ->* 运算符的情况下调用成员函数
Is there a way to call member function without .* or ->* operator
下面通过指针到成员函数调用D::foo
函数的方法将生成错误:必须使用.*
或->*
来调用"f (...("中的指针到成员函数..当然,这不是我们调用指向成员的指针函数的方式。
正确的呼叫方式是(d.*f)(5);
或(p->*f)(5);
我的问题是,'有没有办法在没有类对象在左侧的情况下调用类的成员函数?我想知道我们是否可以传递类对象(this
(作为常规参数?
在我看来,归根结底(在汇编/二进制级别(,类的所有成员函数都是普通函数,应该对 n + 1 个参数进行操作,其中 (+1 表示this
(
如果我们在下面谈论D::foo
函数,在汇编/二进制级别,它应该对两个参数进行操作:
- 类
- 对象本身(指向名为
this
的类 D 对象的指针( - 和
int
.
那么,有没有办法(或hack(使用作为函数参数传递给它的类对象来调用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)) ();
编辑:"请注意,我不是在寻找这个程序的有效版本,我知道如何让它工作">
你真的想在不使用.
或->
的情况下调用成员函数?真的,真的吗?好吧,好吧...
邪恶:
#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; }
邪恶:
#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
作为最后一个参数。
因此,如果您想将成员函数视为具有额外this
的普通函数,那么您必须将自己限制为特定的 ABI。这篇文章提供了一个示例,说明您可以如何做到这一点(或者更确切地说,是为什么您真的不应该这样做的示例!
那么,有没有办法(或hack(调用D::foo,将类对象作为函数参数传递给它,而不是在类对象上使用.或->或.*或->*运算符?
一个特定于平台的,令人作呕的肮脏黑客...
我不太确定你在问什么。通过指向成员的指针调用函数没有"人为限制";您只需要使用正确的语法:
(d.*f)(5); // for objects or references
(p->*f)(5); // for pointers
bind
没有做任何"魔术";在它的实现中,它正是这样做的。
归根结底,它是一个接受两个参数的函数
不,它是一个成员函数,它接受一个参数,并在对象上调用。虽然在概念上类似于采用两个参数的函数,但有一个很大的区别:成员函数可以是虚拟的,涉及运行时调度机制。
我看到的唯一方法是将typedef-ed函数指针替换为行为类似于函数的类。
你在寻找这样的东西吗?
在下面的代码中,我使用宏来typedef模板,然后像您描述的函数一样运行。
#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;
}
有没有办法在没有类对象的情况下调用类的成员函数?
两种方式无需组装:
#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;
}
但必须有一种方法来避免这种人为的限制。 由 C++ 强加
"人为限制">是什么意思?这只是语言定义的语法。怎么了?bind()
的"魔力"将在内部使用 ->*
运算符来调用函数。
是的,C++11 标准中有一个名为 INVOKE 的抽象,它适用于可调用对象。指针到成员函数 (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);
}
http://ideone.com/VPWV5U
是的,确实,只要有足够的扭曲,您就可以逃避"新"成员函数语法,但我不得不问:您为什么要这样做? 该语法有助于理解成员函数以某种方式从周围对象调用。 鉴于虚函数的存在,这实际上是(并且必须(的情况。 它还会自动更改调用虚拟函数(以及对虚拟继承的支持(所需的 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 和朋友正在做的事情非常相似,但通常更快(尽管显然,不太便携(。
在这里使用的模板和运算符重载下面有一些数学(在绑定函数内部(,它弄清楚如何将 &someInstance 更改为 SomeClass::someInstance 所需的指针。 它还查找基础函数的实际地址,并记录这两个值供以后使用。 当调用发生时,它会通过使用->*
的一些技巧迫使编译器"做正确的事情"。 但是,如果你真的想避免依赖->*
运算符,你可以在这一点上做一些类型转换并将指针变成"__thiscall"函数指针(好吧,假设你在Windows上(:
__thiscall调用约定用于成员函数,并且 C++成员函数使用的默认调用约定 不使用变量参数。在__thiscall下,被叫方清理 堆栈,这对于 vararg 函数来说是不可能的。参数被推送 在堆栈上从右到左,传递 this 指针 通过寄存器 ECX,而不是在堆栈上,在 x86 架构上。
那么,你到底不喜欢什么:someInstance->someFunction((?
在这个问题上,标准异常清晰,完全明确。第5.2.2(C++11(节第一句规定:
函数调用有两种:普通函数调用和成员函数调用。
就是这样 - 它们是不同的东西,你不能把它们混为一谈。
这是您的程序的一个有效版本。 指向类方法的指针需要一些复杂的调用,如下所示:
#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 . 或 ->,以下方法将起作用
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;
}
有没有办法在没有类对象的情况下调用类的成员函数?
。
那么,有没有办法(或hack(使用作为函数参数传递给它的类对象来调用
D::foo
,而不是在类对象上使用.
或->
或.*
或->*
运算符?
简而言之,没有。如果不以某种方式在调用的左侧指定对象,则无法调用非static
类成员函数。如果未指定类(对于静态函数(或对象(对于非静态函数(,则编译器无法知道您尝试调用哪个函数。它位于当前范围之外,因为它位于类或对象内。有一些方法可以"破解"代码,以便您可以将其编写为看起来像示例中的main()
。其他一些答案给出了这些黑客的例子。
------------使用静态函数-----------------
可以在类外部调用类中的函数,而无需指定类对象。执行此操作的方法是为函数指针创建一个具有与类函数匹配的签名的typedef
,然后创建这样的指针并为其分配类函数的地址。然后可以调用该函数,而无需在左侧指定类或对象。
该函数必须static
。ISO C++ 不允许获取绑定成员函数的地址来形成指向该函数的指针,即不能从类对象创建指向非static
成员函数的指针。您可能会找到一些不符合ISO标准的编译器,但我对此没有任何指导。
非static
类成员函数this
参数是隐含参数,并且始终是隐含参数。不能手动指定它,但可以通过传递指向类对象的指针来模拟它。
因此,对于您的代码示例:
-
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);
虽然我认为这是一种奇怪的事情,但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;
}
- 为什么Mat类的两个对象可以在不重载运算符+的情况下添加
- 为什么我可以在不重载 "=" 运算符的情况下将一个对象分配给另一个对象?
- 如何在不使用乘除运算符的情况下计算 A 的 B 次幂?
- 如何在没有算术运算符的情况下重载
- 如何在不使用赋值运算符的情况下为动态变量赋值?
- C++箭头运算符 (->) 是否在所有情况下都返回左值?
- 为什么括号在这种情况下无法更改c++运算符的优先级
- 如何在没有运算符>>的情况下从字符串流返回下一个令牌?
- 在不放置新运算符的情况下,在预分配的内存上使用虚函数初始化对象 - 这可能吗?如果没有,为什么
- 在不使用 * 和 / 运算符的情况下将两个浮点数相乘和除以
- 在以下情况下,由于&运算符会发生什么?
- 为什么我可以在不使用赋值运算符的情况下使用列表初始化普通数组
- 范围分辨率运算符在类型:: var的情况下返回什么
- 在没有比较运算符的情况下查找 2 个数字之间的最小值
- 为什么我得到"C2678 二进制'+':没有找到采用类型为"const 的左操作数......"的运算符。"在这种情况下?
- 为什么友元运算符<<在某些情况下使用,而在其他情况下不使用
- C++是否提供了一种在没有范围解析运算符的情况下访问类中的类的方法?
- 如何在不修改C 中的LHS参数的情况下实现` `运算符
- 运算符重载如何工作,为什么在我的情况下不起作用?
- 是否可以超载[]运算符在不定义类的情况下访问特定的字符