I recently attempted to create an is_class
class and needed a way for the compiler to differentiate between enumeration types and class types for which conversion operators are defined. Seeing as how classes, structs and unions are the only types compatible with pointer-to-member functions, I decided to have the compiler determine if the type used to instantiate the is_class
template was, in turn, compatible with pointers-to-member functions. After running into several issues, I decided to test the behavior of enumerations when used in conjunction with pointer-to-members and got some wacky results. The following segment illustrates the first quirk:
enum ENUM {};
void Test(void (ENUM::*pmem) (void))
{
/* ... */
}
Test(NULL);
When compiling with Microsoft Visual C++ 2010, the pointer-to-member portion of the function definition: (ENUM::*pmem)
is highlighted in red and mousing over the declaration reveals the error:
Error: "ENUM" is not a class type
However, the compiler parses this segment without encountering any errors, assigning pmem
to NULL
. It is interesting to me that the compiler would allow this seeing as how enumeration types are not classes, structs or unions and therefore cannot possess methods of their own.
The second point of interest arose when creating a template function, taking a pointer-to-member argument whose type varies:
template<class _Ty>
void Test_Template(void (_Ty::*pmem) (void))
{
/* ... */
}
Of course in order to use this function, it must be explicitly qualified:
Test_Template<ENUM>(NULL);
This call however, generates an error stating:
invalid explicit template argument(s) for 'void Test(void (__thiscall _Ty::* )(void))'
I fixed this issue by creating an additional function template, the prototype of which would match any call that failed to match the prototype for the former template function (which involved using an ellipsis).
Questions:
Why is an enumeration compatible with pointers-to-members?
Why is there an exact match when invoking the non-template Test
function while the compiler generates an error for the template Test_Template
explicit qualification?
Pointers to members allow you to refer to nonstatic members of class objects. You cannot use a pointer to member to point to a static class member because the address of a static member is not associated with any particular object. To point to a static class member, you must use a normal pointer.
Using a pointer-to-member-function to call a function Calling the member function on an object using a pointer-to-member-function result = (object. *pointer_name)(arguments); or calling with a pointer to the object result = (object_ptr->*pointer_name)(arguments);
A pointer to member may not point to a static member of the class, a member of reference type, or void . A pointer to a member of a class differs from a normal pointer: it has both type information for the type of the member and for the class to which the member belongs.
A pointer declaration names a pointer variable and specifies the type of the object to which the variable points. A variable declared as a pointer holds a memory address.
With regards to your first question, it seems like the compiler is indeed reporting that enums can't have member functions, since the compiler is reporting an error on the function declaration. It's probably letting the call succeed by internally trying to correct the bad declaration as much as possible, which in this case means noticing that you were trying to declare something pointer-like and allowing the call. There's no requirement that the compiler give you an error on that line; since the program is I'll-formed, as long as the compiler rejects the program with a diagnostic it doesn't need to give errors everywhere.
As for your second question, the reason that having a second template makes the error go away is the "substitution failure is not an error" (SFINAE) principle. When the compiler instantiates a function template with some type arguments, if it finds that a particular function instantiation is invalid (for example, trying to get a pointer to a member of an enum), it doesn't report an error. Instead, it just removes that template from consideration. If, however, none of the templates you've written are valid when instantiated with the given arguments, then the compiler will issue am error because it can't find a match for what you're trying to do. In the first case, when you have just one template, the error occurs because SFINAE eliminates the only template candidate from consideration, causing the template instantition to have no matching template. In the second case, your "catch-all" template is still valid after you instantiate the template, so while the template taking a pointer-to-member is excluded, there is still a legal template that you can refer to. Consequently, the code is perfectly fine.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With