Strange C++ rule for member function pointers? [duplicate] - c++

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Error with address of parenthesized member function
In this recent question the OP ran into a strange provision of the C++ language that makes it illegal to take the address of a member function if that member function name is parenthesized. For example, this code is illegal:
struct X {
void foo();
};
int main() {
void (X::* ptr)();
ptr = &(X::foo); // Illegal; must be &X::foo
}
I looked this up and found that it's due to §5.3.1/3 of the C++ ISO spec, which reads
A pointer to member is only formed when an explicit & is used and its operand is a qualified-id not enclosed in parentheses [...]
Does anyone have any idea why the spec has this rule? It's specific to pointers-to-member, so I would suspect that there is some grammatical ambiguity that this resolves, but I honestly haven't the faintest idea what it might be.

This is just a personal opinion.
If &(qualified-id) is allowed as &(unary-expression),
qualified-id has to be an expression, and an expression is expected to have a type
(even if it is incomplete).
However, C++ didn't have a type which denotes a member, had only
a pointer to member.
For example, the following code cannot be compiled.
struct A { int i; };
template< class T > void f( T* );
int main() {
(void) typeid( A::i );
f( &A::i );
}
In order to make &(qualified-id) be valid, the compiler has to hold
a member type internally.
However, if we abandon &(qualified-id) notation, the compiler doesn't need
to handle member type.
As member type was always handled in the form of a pointer to it,
I guess the standard gave priority to simplify the compiler's type
system a little.

Imagine this code:
struct B { int data; };
struct C { int data; };
struct A : B, C {
void f() {
// error: converting "int B::*" to "int*" ?
int *bData = &B::data;
// OK: a normal pointer
int *bData = &(B::data);
}
};
Without the trick with the parentheses, you would not be able to take a pointer directly to B's data member (you would need base-class casts and games with this - not nice).
From the ARM:
Note that the address-of operator must be explicitly used to get a pointer to member; there is no implicit conversion ... Had there been, we would have an ambiguity in the context of a member function ... For example,
void B::f() {
int B::* p = &B::i; // OK
p = B::i; // error: B::i is an int
p = &i; // error: '&i'means '&this->i' which is an 'int*'
int *q = &i; // OK
q = B::i; // error: 'B::i is an int
q = &B::i; // error: '&B::i' is an 'int B::*'
}
The IS just kept this pre-Standard concept and explicitly mentioned that parentheses make it so that you don't get a pointer to member.

Related

Disable implicit conversion between typedefs

In C++11, is there a clean way to disable implicit conversion between typedefs, or do you have to do something nasty like wrap your int in a class and define and delete various operators?
typedef int Foo;
typedef int Bar;
Foo foo(1);
Bar bar(2);
bar = foo; // Implicit conversion!
HelloWorld explains why what you have cannot work. You'll need what's typically called a "strong" typedef to do what you want. An example implementation is BOOST_STRONG_TYPEDEF:
#include <boost/serialization/strong_typedef.hpp>
BOOST_STRONG_TYPEDEF(int, a)
void f(int x); // (1) function to handle simple integers
void f(a x); // (2) special function to handle integers of type a
int main(){
int x = 1;
a y;
y = x; // other operations permitted as a is converted as necessary
f(x); // chooses (1)
f(y); // chooses (2)
}
If we had done typedef int a;, then the code would be ambiguous.
The C++ standard says:
7.1.3 The typedef specifier
A name declared with the typedef specifier becomes a typedef-name. Within the scope of its declaration, a typedef-name is syntactically equivalent to a keyword and names the type associated with the identifier in
the way described in Clause 8. A typedef-name is thus a synonym for another type. A typedef-name does
not introduce a new type the way a class declaration (9.1) or enum declaration does
But e.g. class or struct introduce new types. In the following example uniqueUnused does actually nothing but is used to create a different type Value<int, 1> != Value<int, 2>. So maybe this is something you are looking for. Keep in mind there is no guarantee the compiler gets rid of the outer structure! The only guarantee this code gives you it's the same size as int
template<typename T, int uniqueUnused>
struct Value
{
Value() : _val({}) {}
Value(T val) : _val(val) { }
T _val;
operator T&() { return _val; }
// evaluate if you with or without refs for assignments
operator T() { return _val; }
};
using Foo = Value<int, 1>;
using Bar = Value<int, 2>;
static_assert(sizeof(Foo) == sizeof(int), "int must be of same size");
static_assert(sizeof(Bar) == sizeof(int), "int must be of same size");
If you want to create a new type based on a class you can simply go with this example (this doesn't work with scalar types since you can't inherit from ints):
class Foo : public Bar // introduces a new type called Foo
{
using Bar::Bar;
};
I wanted to do something similar to keep different indexes separated not only logically, but also enforced by the compiler. The solution I came up with is basically to just define structs with one element. In some ways it's more painful to use, but it works very well with my situation since I often don't need to deal with the actual value of the index for a lot of my code, just passing it around.
typedef struct{uint16_t i;} ExpressionIndex;
typedef struct{uint16_t i;} StatementIndex;
Now, trying to do
ExpressionIndex foo() {
StatementIndex i;
return i;
}
yields the error error: incompatible types when returning type ‘StatementIndex’ but ‘ExpressionIndex’ was expected
Converting between types is a bit painful, but that was the intent of my change.
ExpressionIndex exp = (ExpressionIndex){stmt.i};
It's not strict type-checking, but illegal conversions can made visible by using original, or Apps Hungarian Notation (H. N.). If you think H. N. means name-type-as-prefix, you're wrong (it's System H. N., and it's, hm, unnecessary naming overhead).
Using the (Apps) H. N., the variable prefix marks not the type (e.g. int), but the purpose, e.g. counter, length, seconds etc. So, when you add a counter to a variable contains elapsed time, you write cntSomethingCounter + secElapsedSinceLastSomething, and you can see that it smells. Compiler does not alerts, but it pokes your eyes.
Read more: http://www.joelonsoftware.com/articles/Wrong.html

About Function Pointer in C++

Do these lines mean the same? Both works without any warning messages!
int (*pFunc)() = func1; // I learned this is right.
int (*pFunc)() = &func1; // Works well with an ampersand too.
Why do I have to put an ampersand in this case? Without it, doesn't work!
int (CMyClass::*pMemberFunc)() = &CMyClass::memberFunc1;
Why do I have to specify namespace for functions in Classes even if the type of the function pointer matches exactly?
int (*pMemberFunc)() = CMyClass::memberFunc1; // Compiler error
int (*pMemberFunc)() = &CMyClass::memberFunc1; // Compiler error
Why can't I specify namespace in this case?
namespace myNamespace {
int func1() {}
}
int (myNamespace::*pFunc)() = myNamespace::func1; // Compiler error
int (myNamespace::*pFunc)() = &myNamespace::func1; // Compiler error
int (*pFunc)() = &myNamespace::func1; // Works!
Your first question, legalese of the Standard:
A pointer to member is only formed when an explicit & is used and its operand is a qualified-id not enclosed
in parentheses. [Note: that is, the expression &(qualified-id), where the qualified-id is enclosed in
parentheses, does not form an expression of type “pointer to member.” Neither does qualified-id, because
there is no implicit conversion from a qualified-id for a non-static member function to the type “pointer to
member function” as there is from an lvalue of function type to the type “pointer to function” (4.3). Nor is
&unqualified-id a pointer to member, even within the scope of the unqualified-id’s class. —end note ]
While it seems inconsistent at first, I do like the fact that it makes the semantics for pointers to members (be them functions or not) equivalent. This certainly has benefits when dealing with templates and decltype.
For the second question, you scope the pointer variable with CMyClass because yours is not a simple "() -> int" function: memberFunc1() is implicitly passed a CmyClass* argument you normally refer to as "this".
If you could truly pass nothing, you'd be missing information (and possibly crash) for the method to do its job correctly. If you're accustomed to "delegates" in other languages, do remember these can optionally store a "Target" pointer to the "this" object if the method is not static/global.
As for the third, it's a free standing function, so it's truly () -> int, but you're attempting to scope your pointer to the namespace, when in fact you're not declaring your variable inside the namespace block.
While the namespace certainly alters how symbols are searched for, it doesn't affect the convention call of the function at all.
1) about Q1, looking at the following code, as the func1 is a right-value of the function, so with or without "&" doesn't change the actual function address.
Look at the following code:
#include <stdio.h>
void foo(){
printf("foo called\n");
}
int main(){
printf("%p\n", foo);
printf("%p\n", &foo);
void (*FUNC) ();
FUNC = foo;
FUNC();
printf("address %p\n", FUNC);
printf("address %p\n", &FUNC);
return 0;
}
output is
0x101406eb0
0x101406eb0
foo called
address 0x101406eb0
address 0x7fff5e7f9a80
Q2 & Q3 )
Pointers to Member Functions Are Not Pointers
You can refer it here
https://www.safaribooksonline.com/library/view/c-common-knowledge/0321321928/ch16.html

How does template converting operator to pointer to member function syntax work

I was looking at the emulated version of nullptr and saw this converting operator (a member of nullptr_t):
template<class C, class T> // or any type of null
operator T C::*() const // member pointer...
{ return 0; }
This syntax for pointer to member function confuses me. I usually expect to see such a type as something like
R (C::*)(I1, I2, ...)
With the template above, there's no input arguments. I can't figure out how the type deduction works in this case. I'm having trouble forming a specific question, other than, how does this work? If I have code like this:
typedef int (MyClass::*MyTypedef)(float);
MyTypedef m = nullptr;
I'm guessing T deduces to int, and C deduces to MyClass. What "happens" to float?
That is a pointer to member, not necessarily a pointer to member function. The difference is that it can generate a pointer to member function or a pointer to non-function member.
Now in the particular use case, the destination is a pointer to member, the compiler is seeing an expression in which it needs a int (MyClass::*)(float), and on the other hand it has a nullptr. It tries to find a conversion and it finds the operator T C::*(), which is a valid conversion if C is deduced to be MyClass and T is deduced to be int (float) [function taking a float and returning an int].
I also find this particular corner of the language a bit confusing (having typedefs, or deduced types for functions), for example this is legal if weird:
typedef void int_f(int);
struct X {
int_f m;
};
void X::m(int x) { std::cout << x << '\n'; }
The same thing is going on in the conversion operator that you are concerned with.

Error with address of parenthesized member function

I found something interesting. The error message says it all. What is the reason behind not allowing parentheses while taking the address of a non-static member function? I compiled it on gcc 4.3.4.
#include <iostream>
class myfoo{
public:
int foo(int number){
return (number*10);
}
};
int main (int argc, char * const argv[]) {
int (myfoo::*fPtr)(int) = NULL;
fPtr = &(myfoo::foo); // main.cpp:14
return 0;
}
Error: main.cpp:14: error: ISO C++ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to member function. Say '&myfoo::foo'
From the error message, it looks like you're not allowed to take the address of a parenthesized expression. It's suggesting that you rewrite
fPtr = &(myfoo::foo); // main.cpp:14
to
fPtr = &myfoo::foo;
This is due to a portion of the spec (§5.3.1/3) that reads
A pointer to member is only formed when an explicit & is used and its operand is a qualified-id not enclosed in parentheses [...]
(my emphasis). I'm not sure why this is a rule (and I didn't actually know this until now), but this seems to be what the compiler is complaining about.
Hope this helps!
Imagine this code:
struct B { int data; };
struct C { int data; };
struct A : B, C {
void f() {
// error: converting "int B::*" to "int*" ?
int *bData = &B::data;
// OK: a normal pointer
int *bData = &(B::data);
}
};
Without the trick with the parentheses, you would not be able to take a pointer directly to B's data member (you would need base-class casts and games with this - not nice).
From the ARM:
Note that the address-of operator must be explicitly used to get a pointer to member; there is no implicit conversion ... Had there been, we would have an ambiguity in the context of a member function ... For example,
void B::f() {
int B::* p = &B::i; // OK
p = B::i; // error: B::i is an int
p = &i; // error: '&i'means '&this->i' which is an 'int*'
int *q = &i; // OK
q = B::i; // error: 'B::i is an int
q = &B::i; // error: '&B::i' is an 'int B::*'
}
The IS just kept this pre-Standard concept and explicitly mentioned that parentheses make it so that you don't get a pointer to member.

Why this is not a vexing parse?

Basically this is a follow up of this question about most vexing parse. I can understand that this is due to the ambiguity between the function declaration and variable definition.
But in Comeau online, I just tired the following.
class T{
public:
T(int i){
}
int fun1(){
return 1;
}
};
int main()
{
T myT(10); // I thought it'd be a function declaration that takes an int and returns a type T
myT.fun1(); // and a compiler error out here.
}
But it compiles fine and there were no errors. I looked into the standard docs but couldn't come to a reasoning.
So, what am I missing here?
Because 10 is not a type. :)
This would be a Most Vexing Parse:
T myT(T());
// T() gets interpreted as function pointer argument to a function returning T();
// This is equivalent to:
T myT(T (*fn)());
Another variety of the Most Vexing Parse is this one:
unsigned char c = 42;
T myT(int(c));
// int(c) gets interpreted as an int argument called c.
// This is equivalent to:
T myT(int c);
The 10 cannot be a parameter type name, so this must be a variable declaration.
The compiler must choose a function declaration when it can do that, but in many cases like this it cannot and there is no ambiguity.
It's not a vexing parse because you used an integer literal rather than, say:
T myT(T());
As in this complete example:
#include <iostream>
struct T { int f() { return 1; } };
int main(int argc, char** argv) {
T t(T());
std::cout << t.f() << '\n';
return 0;
}
Which is ambiguous because it could mean:
myT is a T initialised with a default-constructed T; or
myT is a function returning a T and taking one argument of type T(), which denotes a zero-argument function whose return type is also T.
The latter interpretation is the default one, which is why a compiler error results from attempting to use the newly declared function as though it were the object you expected it to be.
See the Wikipedia article about it.