可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I could be totally wrong here, but as I understand it, C++ doesn't really have a native "pointer to member function" type. I know you can do tricks with Boost and mem_fun etc. But why did the designers of C++ decide not to have a 64-bit pointer containing a pointer to the function and a pointer to the object, for example?
What I mean specifically is a pointer to a member function of a particular object of unknown type. I.E. something you can use for a callback. This would be a type which contains two values. The first value being a pointer to the function, and the second value being a pointer to the specific instance of the object.
What I do not mean is a pointer to a general member function of a class. E.G.
int (Fred::*)(char,float)
It would have been so useful and made my life easier.
Hugo
回答1:
@RocketMagnet - This is in response to your other question, the one which was labeled a duplicate. I'm answering that question, not this one.
In general, C++ pointer to member functions can't portably be cast across the class hierarchy. That said you can often get away with it. For instance:
#include <iostream>
using std::cout;
class A { public: int x; };
class B { public: int y; };
class C : public B, public A { public: void foo(){ cout << "a.x == " << x << "\n";}};
int main() {
typedef void (A::*pmf_t)();
C c; c.x = 42; c.y = -1;
pmf_t mf = static_cast<pmf_t>(&C::foo);
(c.*mf)();
}
Compile this code, and the compiler rightly complains:
$ cl /EHsc /Zi /nologo pmf.cpp
pmf.cpp
pmf.cpp(15) : warning C4407: cast between different pointer to member representations, compiler may generate incorrect code
$
So to answer "why doesn't C++ have a pointer-to-member-function-on-void-class?" is that this imaginary base-class-of-everything has no members, so there's no value you could safely assign to it! "void (C::)()" and "void (void::)()" are mutually incompatible types.
Now, I bet you're thinking "wait, i've cast member-function-pointers just fine before!" Yes, you may have, using reinterpret_cast and single inheritance. This is in the same category of other reinterpret casts:
#include <iostream>
using std::cout;
class A { public: int x; };
class B { public: int y; };
class C : public B, public A { public: void foo(){ cout << "a.x == " << x << "\n";}};
class D { public: int z; };
int main() {
C c; c.x = 42; c.y = -1;
// this will print -1
D& d = reinterpret_cast<D&>(c);
cout << "d.z == " << d.z << "\n";
}
So if void (void::*)()
did exist, but there is nothing you could safely/portably assign to it.
Traditionally, you use functions of signature void (*)(void*)
anywhere you'd thing of using void (void::*)()
, because while member-function-pointers don't cast well up and down the inheritance heirarchy, void pointers do cast well. Instead:
#include <iostream>
using std::cout;
class A { public: int x; };
class B { public: int y; };
class C : public B, public A { public: void foo(){ cout << "a.x == " << x << "\n";}};
void do_foo(void* ptrToC){
C* c = static_cast<C*>(ptrToC);
c->foo();
}
int main() {
typedef void (*pf_t)(void*);
C c; c.x = 42; c.y = -1;
pf_t f = do_foo;
f(&c);
}
So to your question. Why doesn't C++ support this sort of casting. Pointer-to-member-function types already have to deal with virtual vs non-virtual base classes, and virtual vs non-virtual member functions, all in the same type, inflating them to 4*sizeof(void*) on some platforms. I think because it would further complicate the implementation of pointer-to-member-function, and raw function pointers already solve this problem so well.
Like others have commented, C++ gives library writers enough tools to get this done, and then 'normal' programmers like you and me should use those libraries instead of sweating these details.
EDIT: marked community wiki. Please only edit to include relevant references to the C++ standard, and add in italic. (esp. add references to standard where my understanding was wrong! ^_^ )
回答2:
As others pointed out, C++ does have a member function pointer type.
The term you were looking for is "bound function". The reason C++ doesn't provide syntax sugar for function binding is because of its philosophy of only providing the most basic tools, with which you can then build all you want. This helps keep the language "small" (or at least, less mind bogglingly huge).
Similarly, C++ doesn't have a lock{} primitive like C#'s but it has RAII which is used by boost's scoped_lock.
There is of course the school of thought that says you should add syntax sugar for everything that might be of use. For better or worse, C++ does not belong to that school.
回答3:
I think what you are looking for might be in these librairies...
Fast Delegates http://www.codeproject.com/KB/cpp/FastDelegate.aspx
Boost.Function http://www.boost.org/doc/libs/1_37_0/doc/html/function.html
And here is a very complete explanation of function pointer related questions http://www.parashift.com/c++-faq-lite/pointers-to-members.html
回答4:
It does.
For example,
int (Fred::*)(char,float)
is a pointer to a member function of a class Fred
that returns an int
and takes a char
and a float
.
回答5:
I think that the answer is that the designers of C++ choose not to have in language the things that could be just as easily implemented in a library. Your own description of what you want gives a perfectly reasonable way to implement it.
I know it sounds funny, but C++ is a minimalistic language. They did leave to libraries all they could leave to them.
回答6:
The TR1 has std::tr1::function, and it will be added to C++0x. So in a sense it does have it.
One of the design philosophies of C++ is: you don't pay for what you don't use. The problem with C# style delagates is they are heavy and require language support, that everyone would pay for whether they used them or not. That is why the library implementation is preferred.
The reason delegates are heavy is that a method pointer is often larger than a normal pointer. This happens whenever the method is virtual. The method pointer will call a different function depending on what base class uses it. That requires at least two pointers, the vtable and the offset. There is other weirdness involved with the method is from a class involved in multiple inheritance.
All that said, I am not a compiler writer. It may have been possible to make a new type for bound method pointers that would subvert the virtual-ness of method referenced (after all we know what the base class is if the method is bound).
回答7:
The issue is surely not the basics of having an object pointer and a function pointer in one easy-to-use package, because you could do this using a pointer and a thunk. (Such thunks are already used by VC++ on x86 to support pointers to virtual member functions, so that these pointers only take up 4 bytes.) You might end up with a lot of thunks, it's true, but people already rely on the linker to eliminate duplicate template instantiations -- I do, anyway -- and there's only so many vtable and this offsets you'll end up with in practice. The overhead would probably not be significant for any reasonably-sized program, and if you don't use this stuff then it won't cost you anything.
(Architectures that traditionally use a TOC would store the TOC pointer in the function pointer part, rather than in the thunk, just as they would have to do already.)
(This new type of object would not be exactly substitutable for a normal pointer to function, of course, because the size would be different. They would, however, be written the same at the call point.)
The problem that I see is that of the calling convention: supporting pointers to functions this way might be tricky in the general case, because the generated code would have to prepare the arguments (this included) the same way without regard to the actual type of thing, function or member function, to which the pointer points.
This is probably not a big deal on x86, at least not with thiscall, because you could just load ECX regardless and accept that if the calling function doesn't need it then it will be bogus. (And I think VC++ assumes ECX is bogus in this case anyway.) But on architectures that pass arguments for named parameters in registers to functions, you may end up with a fair amount of shuffling in the thunk, and if stack arguments are pushed left-to-right then you're basically stuffed. And this can't be fixed up statically, because in the limit there's no cross-translation-unit information.
[Edit: MSalters, in a comment to rocketmagnet's post above, points out that if both object AND function are known, then the this offset and so on can be determined immediately. This totally didn't occur to me! But, with this in mind, I suppose there need only be stored the exact object pointer, maybe offset, and the exact function pointer. This makes thunks totally unnecessary -- I think -- but I'm pretty sure that the issues of pointing to member functions and non-member functions alike would remain.]
回答8:
C++ is already a big language, and adding this would have made it bigger. What you really want is even worse than just a bound member function, it's something closer to boost::function. You want to store both a void(*)()
and a pair for callbacks. After all, the reason is that you want the give the caller a complete callback, and the callee should not care about the exact details.
The size would likely be sizeof(void*)+sizeof(void(*)())
. Pointers to member functions can be bigger, but that is because they are unbound. They need to deal with the possibility that youre' taking the address of a virtual function, for instance. However, a built-in bound-pointer-to-member-function type would not suffer from this overhead. It can resolve the exact function to be called at the moment of binding.
This is not possible with a UDT. boost::function cannot discard the overhead of a PTMF when it binds the object pointer. You need to know understand structure of a PTMF, vtable, etcetera - all non-standard stuff. However, we might get there now with C++1x. Once it's in std::, it's fair game for compiler vendors. The standard library implementation itself is not portable (see e.g. type_info).
You'd still want to have some nice syntax, I guess, even if a compiler vendor implements this in the library. I'd like std::function<void(*)()> foo = &myX && X::bar
. (It doesn't clash with existing syntax, as X::bar is not an expression - only &X::bar is)
回答9:
It occurs to me that methods have an implicit this
argument, so a c
pointer to a method is insufficient to allow the method to be called (because there is no way to determine which instance should be used for this
(or even if any instance is currently extant)).
Edit: Rocketmagnet comments that he addressed this in the question, and that appears to be the case, though I think that was added after I started this response. but I'll say "mea culpa", anyway.
So allow me to expand on the thought a bit.
C++ is closely related to c, and has all its intrinsic types compatible with the earlier language (largely because of the history of c++
development, I suppose). So, a intrinsic c++
pointer is a c
pointer, and is incapable of supporting the use you ask for.
Certainly you could build a derived type to do the job---as in the boost implementation---but such a critter belongs in a library.