Can anyone enlighten me on the status of member function references?
Does the C++ language specification allow them or not?
In particular, GCC happily compiles the following code without
warnings (and indeed does so very efficiently, which is what I'm
really after here):
class c {
private:
void a(int arg) { /* ... */ }
int b() { /* ... */ }
static int x1(int arg) { /* ... */ }
typedef int c::fcn(int);
void f(fcn& x) { a(x(b())); }
public:
void test() { f(x1); }
};
Since I have a feeling that this code is not really valid C++, I'd
also appriciate any suggestions on how to express this differently.
A bit of background:
In reality, I have quite a few xn()'s and a handful of f()'s which are
really more complex than the example, and since they're combined all
over the place I desire a shorthand notation for the combination of
f() and xn() without having to create a separate member function for
each possible combination (there are several hundreds).
I do need them to inline, and I don't want to make function objects
out of the xn()'s since they _do_ modify the state of c, and thus
belongs as member functions of c.
A few macros does, of course, work, but is obviously less than
completely elegant.
Cheers,
--
Christer Palm
ISO/IEC 14882:1998(E)
8.3.3
3 A pointer to member shall not point to a static member of
a class (9.4), a member with reference type, or "cv void."
[Note: see also 5.3 and 5.5. The type "pointer to member"
is distinct from the type "pointer", that is, a pointer to
member is declared only by the pointer to member declarator
syntax, and never by the pointer declarator syntax. There
is no "referenceè² oè¡«ember" type in C++. ]
>
> In particular, GCC happily compiles the following code without
> warnings (and indeed does so very efficiently, which is what I'm
> really after here):
Must be an extension. Perhaps just put it inside an
#ifdef <whatever GCC happens to use to identify itself>
>
> class c {
> private:
> void a(int arg) { /* ... */ }
> int b() { /* ... */ }
> static int x1(int arg) { /* ... */ }
>
> typedef int c::fcn(int);
> void f(fcn& x) { a(x(b())); }
>
> public:
> void test() { f(x1); }
> };
>
> Since I have a feeling that this code is not really valid C++, I'd
> also appriciate any suggestions on how to express this differently.
>
> A bit of background:
> In reality, I have quite a few xn()'s and a handful of f()'s which are
> really more complex than the example, and since they're combined all
> over the place I desire a shorthand notation for the combination of
> f() and xn() without having to create a separate member function for
> each possible combination (there are several hundreds).
>
> I do need them to inline, and I don't want to make function objects
> out of the xn()'s since they _do_ modify the state of c, and thus
> belongs as member functions of c.
>
> A few macros does, of course, work, but is obviously less than
> completely elegant.
Why not just use pointers instead of references?
-Mike
They are allowed. The only kinds of references not allowed:
"There shall be no references to references, no arrays of references, and no
pointers to references."
...
"A reference shall be initialized to refer to a valid object or function."
--
Ioannis
* Programming pages: http://www.noicys.freeurl.com
* Alternative URL: http://run.to/noicys
That is you can point with a reference to it directly. Consider the code:
class C
{ public:
int x;
int &s() { return x; }
};
int main()
{
C c={7};
int &x=c.x;
int &y=c.s();
}
> Must be an extension. Perhaps just put it inside an
> #ifdef <whatever GCC happens to use to identify itself>
No, i do not think so.
> Why not just use pointers instead of references?
Well he can use references.
From above:
"There is no "reference to Âmember" type in C++. "
Plain enough to me.
>Consider the code:
>
> class C
> { public:
> int x;
>
> int &s() { return x; }
This is not a reference to a member function.
It's a function which returns a reference to type int.
> };
>
>
>
> int main()
> {
> C c={7};
>
> int &x=c.x;
A reference to int, initialized with the value
of c::x. Not a reference-to-member.
>
> int &y=c.s();
A reference to int, initialized with the return value
of c::s(). Not a reference-to-member.
>
> }
>
>
>
>
>
> > Must be an extension. Perhaps just put it inside an
> > #ifdef <whatever GCC happens to use to identify itself>
>
>
> No, i do not think so.
8.3.3 / 3
"There is no "reference to Âmember" type in C++. "
If such a beast is supported by an implementation,
it is an extension, not part of the standard language.
>
>
>
> > Why not just use pointers instead of references?
>
>
> Well he can use references.
Not to a member. It's specifically disallowed.
OP wanted to use:
typedef int c::fcn(int);
void f(fcn& x) { a(x(b())); }
The second line is invalid. (The 'fcn&' parameter)
-Mike
not
> allowed.
See 8.3.3 / 3
> The only kinds of references not allowed:
>
> "There shall be no references to references, no arrays of references, and
no
> pointers to references."
And there is no such type as 'reference-to-member'. (8.3.3/3)
>
> ...
>
> "A reference shall be initialized to refer to a valid object or function."
Function, not member function.
-Mike
Yes it is a reference to a data member. And all the others.
> 8.3.3 / 3
> "There is no "reference to Âmember" type in C++. "
>
> If such a beast is supported by an implementation,
> it is an extension, not part of the standard language.
Look i am not sure, perhaps you are right. However my feeling is that
references to member functions when used as arguments to other member
functions are allowed. It is very late here for me to think properly, but in
standard library at least references to regular functions are used heavily
(when functions are passed instead of function objects).
After i recharge i 'll deal with it. :)
Members are not only the member functions but also the data members. True
there is no such notation as reference to member, however for example for
data members we can point to them with regular references. For the member
functions i shall answer tomorrow. :)
I did not go by feeling, but by looking it up
in the standard, which explicitly disallows
'reference-to-member.'
> It is very late here for me to think properly,
I didn't need much thinking, just reading.
>but in
> standard library at least references to regular functions are used heavily
Sure. That's valid.
> (when functions are passed instead of function objects).
>
> After i recharge i 'll deal with it. :)
:-)
-Mike
Right. From 8.3.3 / 3
"There is no "referenceÂ-to-Âmember" type in C++."
which of course includes both data members and
member functions.
> True
> there is no such notation as reference to member, however for example for
> data members we can point to them with regular references.
Right, references to whatever their type is.
>For the member
> functions i shall answer tomorrow. :)
I've already answered it several times.
-Mike
I suspect the OP wanted to know about _declaring_ something to be a
reference to a member. Declaring an ordinary reference and making it refer
to a data member does not make it a reference to a member, because the
declared reference was not confined to referring only to members.
David
"Mike Wahler" <mkwa...@mkwahler.net> wrote in message news:<b0295h$f73$1...@slb0.atl.mindspring.net>...
> Christer Palm <pa...@nogui.se> wrote in message
> news:71782a34.03011...@posting.google.com...
> >
> > In particular, GCC happily compiles the following code without
> > warnings (and indeed does so very efficiently, which is what I'm
> > really after here):
>
> Must be an extension.
Well, that's what I'm afraid of. OTOH, it is _not_ mentioned in the
list of extensions to the C++ language in the GCC docs
(http://gcc.gnu.org/onlinedocs/gcc-3.2.1/gcc/C---Extensions.html#C++%20Extensions).
> Perhaps just put it inside an
> #ifdef <whatever GCC happens to use to identify itself>
>
That's exactly what I intend to do (and use macros as an alternative)
if it turns out this is not valid C++.
>
> Why not just use pointers instead of references?
>
Simply because it generates extremely inefficient code (at least with
GCC) - even with declaring the argument to f() as a const pointer. I
really need this construct to inline the involved functions.
--
Christer Palm
is equivelent to
typedef int fcn(int);
> void f(fcn& x) { a(x(b())); }
is equivelent to
void f(void (&fcn)(int) x) { a(x(b())); }
is equivelent to
void c::f(void (&fcn)(int) x) { a(x(b())); }
Note sticking 'c::' in there doesn't make any difference it's just
redundant scope resolution.
>
> public:
> void test() { f(x1); }
> };
>
> Since I have a feeling that this code is not really valid C++, I'd
> also appriciate any suggestions on how to express this differently.
It is valid C++, it just isn't doing what you (seem) think it's doing.
>
> A bit of background:
> In reality, I have quite a few xn()'s and a handful of f()'s which are
> really more complex than the example, and since they're combined all
> over the place I desire a shorthand notation for the combination of
> f() and xn() without having to create a separate member function for
> each possible combination (there are several hundreds).
>
> I do need them to inline, and I don't want to make function objects
Any inlining your getting can only be because GCC has a good
(global) optimizer, once you start having lots of f()'s & xn()'s
It'll probably stop inlining.
> out of the xn()'s since they _do_ modify the state of c, and thus
> belongs as member functions of c.
>
The only way's to pass a function that you want to be inlined
around are functors and macros, as soon as you make it a
pointer (or refrence) its unlikely to be inlined.
Rob.
Yes but you can still do:
class A
{
int x;
int &y;
public:
A(int r):x(r), y(x) {}
};
Yes exactly (and i am 110% recharged).
By functiors you probably mean function objects. Also better avoid macros.
Also using template classes with inline static functions is good for
inlining. For example:
class something
{
public:
static void f()
{
// ...
}
// ...
};
template <class T>
void g()
{
T::f();
}
Functions passed in this way can be easily inlined and also this method has
more efficiency gains. When you want to pass many functions with this way
they are passed faster.
Also using classes with inline static functions passed to template
functions...
struct functor
{
some_type operator() (/* some arglist */)
{
// inline definition.
}
};
template <typename T>
void g(T const &func)
{
func( /*...*/ );
}
inline int normal(/* some arglist */)
{
}
int main()
{
g(functor()); // tempory binds to const.
/* pass a reference to an out of line version of normal()
*/
g(normal);
}
The way you do it bellow is probably better for the OP
as there's no need to create a tempory 1 byte/no constuctor
functor object.
> Also using template classes with inline static functions is good for
> inlining. For example:
>
>
> class something
> {
> public:
> static void f()
> {
> // ...
> }
>
> // ...
> };
>
>
> template <class T>
> void g()
> {
> T::f();
> }
>
>
>
> Functions passed in this way can be easily inlined and also this method has
> more efficiency gains. When you want to pass many functions with this way
> they are passed faster.
>
Rob.
Rob Williscroft <r...@REMOVE-freenet.co.uk> wrote in message news:<3E2569A1...@REMOVE-freenet.co.uk>...
> Christer Palm wrote:
> >
> > typedef int c::fcn(int);
>
> is equivelent to
> typedef int fcn(int);
>
Yes, that seems to be correct. I.e the 'c::' doesnt restrict fcn to
members of c.
I also realize now that it doesn't work unless I declare the x1()
static, which, except for scope, makes it more or less equvivalent to
a non-member function.
So what we really have here is a "reference to function" I guess?
> void f(void (&fcn)(int) x) { a(x(b())); }
> void c::f(void (&fcn)(int) x) { a(x(b())); }
>
I get your point, but I assume you mean 'void f(int (&x)(int))'?
> Any inlining your getting can only be because GCC has a good
> (global) optimizer, once you start having lots of f()'s & xn()'s
> It'll probably stop inlining.
>
I tried it, and indeed GCC stops inlining the xn()'s after having put
the ~30 first inline. :-(
> The only way's to pass a function that you want to be inlined
> around are functors and macros, as soon as you make it a
> pointer (or refrence) its unlikely to be inlined.
:-(
Yeah, I guess that was basically my conclusion too, I was just hoping
that I was wrong.
The problem with functors is that they would not belong to c, which
means that I have to explicitly pass 'this' along to the functor. Not
very nice.
Additionally, the verbosity of functor vs. member function declaration
would seriously bloat the code in this case. We're talking about ~100
3-liners.
So guess I will have to bite the dust and go with the macros.
Why use macros? As a rule of thumb in C++ you should use templates or inline
functions in place of macros.
Avoid macros!
"Ioannis Vranos" <noi...@h01tmail.com> wrote in message news:<1042675964.40547@athprx02>...
> "Christer Palm" <pa...@nogui.se> wrote in message
> news:71782a34.03011...@posting.google.com...
> >
> > So guess I will have to bite the dust and go with the macros.
>
>
> Why use macros? As a rule of thumb in C++ you should use templates or inline
> functions in place of macros.
OK, so perhaps I'm still missing something here...
How can I use inline functions or templates to replace macros for this
particular problem?
Regards,
--
Christer Palm
So if i understand well your problem is that you wish to avoid the time cost
of function calls of a static function, that is the function to be inlined.
Well you can do directly in that definition of yours:
class c {
private:
void a(int arg) { /* ... */ }
int b() { /* ... */ }
==> inline static int x1(int arg) { /* ... */ }
typedef int fcn(int);
void f(fcn& x) { a(x(b())); }
public:
void test() { f(x1); }
};
To push things further:
class c {
private:
void a(int arg) { /* ... */ }
int b() { /* ... */ }
==> inline static int x1(int arg) { /* ... */}
==> template<class T>
inline void f(T &x) { a(x(b())); }
public:
void test() { f(x1); }
};
You can push things even further.
"Ioannis Vranos" <noi...@h01tmail.com> wrote in message news:<1042758902.529733@athprx02>...
>
> To push things further:
>
> class c {
> private:
> void a(int arg) { /* ... */ }
> int b() { /* ... */ }
> ==> inline static int x1(int arg) { /* ... */}
>
> ==> template<class T>
> inline void f(T &x) { a(x(b())); }
>
> public:
> void test() { f(x1); }
> };
>
OK - that is just as the solution I suggested myself in my initial post.
As stated earlier, the problem is that;
1) I don't want x1 to be static.
2) GCC stops inlining xN() after ~30 calls to f().
Regards,
--
Christer Palm
Ok then don't make x1 static. Now i wonder what is the problem in reality.
Since everything is defined inside the class why do you want to pass x1 as
an argument?!
If I do not make x1 static, the code does not compile.
> Now i wonder what is the problem in reality.
> Since everything is defined inside the class why do you want to pass x1 as
> an argument?!
Because it seems that I cannot pass a member function as an argument
without using a member function pointer! GCC (and probably most other
compilers) generates inefficient code if I use function pointers -
hence it seems that I need to use macros instead.
--
Christer Palm
? Can you give me a working example what do you mean by that with a main()?
>
> > Now i wonder what is the problem in reality.
> > Since everything is defined inside the class why do you want to pass x1
as
> > an argument?!
>
> Because it seems that I cannot pass a member function as an argument
> without using a member function pointer! GCC (and probably most other
> compilers) generates inefficient code if I use function pointers -
> hence it seems that I need to use macros instead.
Why should you pass a member function as an argument? I think you have a
great misconception here. Here is an example:
class c {
private:
void a(int arg) { /* ... */ }
int b() { /* ... */ }
int x1(int arg) { /* ... */ }
void f() { a(x1(b())); }
public:
void test() { f(); }
};
class c {
private:
void a(int arg) { /* ... */ }
int b() { /* ... */ }
int x1(int arg) { /* ... */ }
struct {
int operator()(c& cref, int arg)
{ return cref.x1(arg); }
} x1_adapter;
template<class Adapter> void f(Adapter& fcn)
{ a(fcn(*this,b())); }
public:
void test() { f(x1_adapter); }
};
The complication beeing, of course, that I have to add ~200 lines of
code for the adapter definitions - but at least this technique allows
me to keep that bloat separate from the xN() implementations.
Any comments?
--
Christer Palm
I tried this
#include <iostream>
struct C_test
{
int b() { return 2; }
int x1(int n) { return n + 2; }
static int s_x1(C_test *that, int n) { return that->x1(n); }
template <int F(C_test *, int)>
void f() { std::cout << "\nC_test " << F(this, b()) << "\n"; }
};
int main()
{
C_test ct;
ct.f<C_test::s_x1>();
}
The output of g++ -O3 -S appeared to be inlined except for the
cout << calls. You'll need to check if it scales up though.
Alternatively you could change your class above:
struct x1_adapter
{
static int call(c& cref, int arg)
{
return cref.x1(arg);
}
);
template<class Adapter> void f()
{
a(Adapter::call(*this,b()));
}
void test() { f<x1_addapter>(); }
Rob.
Duh...
Stop trolling, or learn how to read and understand the question before
posting an answer, whichever is applicable.
--
Christer Palm
The answer to the original question is that references to member functions
passed to other member functions are allowed. However all member function
names have scope inside the whole class regardless the orfer of
definition/declaration, so passing them to one another is of no use.
Elaborating a bit further on that, I came up with the following
snippet which I think quite clearly demonstrates why the lack of a
"reference to member function" type in C++ seems to be quite stupid:
class c {
private:
void x1() { }
static void x1_s(c& cref) { cref.x1(); }
template <typename T> void f_s(T& x) { x(*this); }
template <typename T> void f(T& x) { x(); }
public:
// OK - calling x1() through static "gateway"
void test_s() { f_s(x1_s); }
// Wrong! Can't make reference to member function
void test() { f(x1);}
};
Can anyone explain the reason to this lack of orthogonality?
To me, this seems like a rather trivial oversight...
--
Christer Palm
f<something>(x1);
You do not provide a type.
My mistake. I 'll think about it later (unless someone else finds the
cause).
class c {
private:
void x1() { }
static void x1_s(c& cref) { cref.x1(); }
template <typename T> void f_s(T const & x) { x(*this); }
template <typename T> void f(T const & x) { (this->*x)(); }
public:
// OK - calling x1() through static "gateway"
void test_s() { f_s(x1_s); }
// Wrong! Can't make reference to member function
void test() { f(&c::x1);}
};
Both of your templates take a pointer. You took a reference to it
and I took a constant reference to it but it's still a pointer.
In f_s() the argument is a function pointer, and in test_s() the
argument x1_s can be implicitly converted.
In f() the argument is going to be a pointer to member function,
and in test() the argument can't be implicitly converted, so you
have to write &c::x1.
Why this is so? I suspect the first case is just the way it was
done in the existing C language. But maybe there's a technical
reason, though I can't think of any.
The other point is you can call a function pointer, say void (*fp)(),
like fp() rather than the more verbose (*fp)(). With a member pointer,
say void (c::*pmf)(), you have to call it like (this->*pmf)().
I guess if C++ were to allow pmf to be implicitly converted it
should also allow it to be implicitly called, but only in a
member function.
Rob.
OK, I get your point, and it is indeed interesting.
However, consider the following code:
void x1() { }
template<typename T> void f1(T x) { cout << typeid(x).name() << endl;
}
template<typename T> void f2(T& x) { cout << typeid(x).name() << endl;
}
int main()
{
f1(x1); // Implicit function pointer - x is PFv_v
f1(&x1); // Explicit function pointer - x is still PFv_v
f2(x1); // Implicit function _reference_ - x is now Fv_v
}
Indeed, passing x1 to f1() causes an implicit conversion to a function
pointer.
But in the case of f2() which explicitly takes a reference argument,
x1 seems to be passed as a reference to function, and not a reference
to a function pointer.
This makes perfect sense, because you cannot have a reference to a
non-variable.
I.e. you cannot do:
int a;
f2(&a); // Error: could not convert `&a' to `int *&'
While this is fine:
int a;
int *p = &a;
f2(p); // OK - x is now Pi
Thus;
f2(&x1);
does not even compile.
So, the bottom line is that I still argue that the code in my previous
post demonstrates the use of a "reference to function" and that there
are no function pointers involved - explicitly or implicitly.
The "reference to function type" is obviously a construct unique to
C++ (in the sense that it does not have a C counterpart). The lack of
this concept for member functions seems strange, because you can have
a pointer to either type.
Do I make sense?
--
Christer Palm
Agreed - I was wrong in describing f_s() as taking a function pointer,
you can write function's like this:
void function(void (&f)()) { f(); }
but to be clear the differences from void function(void (*f)()) is
that in function you cant write (*f)() or f = new_value.
>
> But in the case of f2() which explicitly takes a reference argument,
> x1 seems to be passed as a reference to function, and not a reference
> to a function pointer.
>
> This makes perfect sense, because you cannot have a reference to a
> non-variable.
> I.e. you cannot do:
>
> int a;
> f2(&a); // Error: could not convert `&a' to `int *&'
>
> While this is fine:
>
> int a;
> int *p = &a;
> f2(p); // OK - x is now Pi
>
> Thus;
>
> f2(&x1);
>
> does not even compile.
>
> So, the bottom line is that I still argue that the code in my previous
> post demonstrates the use of a "reference to function" and that there
> are no function pointers involved - explicitly or implicitly.
It does.
>
> The "reference to function type" is obviously a construct unique to
> C++ (in the sense that it does not have a C counterpart). The lack of
> this concept for member functions seems strange, because you can have
> a pointer to either type.
>
> Do I make sense?
>
Yes, but you could also ask another question, why are references to
functions part of C++ since they don't provide any utility that
isn't provided by function pointers. I think the answer is that
it allows a simple function to behave like a 'functor', particularly
a 'reference to a functor', more efficiently. This is important for
the standard library algorithms.
The other question is why are the .* and ->* part of C++, sure you
could argue that function pointers have an equivalent in the (*f)()
syntax, but I have at least one compiler that gives me a diagnostic
telling me its unnecessary if I use it.
<fantasy-C++>
class base
{
void p();
};
class derived: public base
{
void p();
void f(void (base::&p)())
{
p();
this->p();
this->base::p();
this->derived::p();
}
};
class not_derived
{
void f(void (base::&p)())
{
p(); // an error I would hope.
derived *d = whatever;
d->p();
}
};
</fantasy-C++>
Even though I wrote the above in my own fantasy dialect of C++,
the only line were I know whats going on is the error!
If the above code were to be allowed the language would become
far more complecated, paricularly the . and -> operators.
I can't see any real advantage over just using .* and ->*.
Rob.
> The other question is why are the .* and ->* part of C++
Yeah, I guess all this just leads us up to the conclusion that you
reach far too often when exploring these aspects of C++. The C++
language does not itself embrace, but rather emulates many of the
concepts and principles associated with object oriented programming
which results in a lot of annoying irregularities.
The first question you bring up is a perfect example. In C++, a
function looks like an object. You can also have an object that looks
like a function (i.e. a 'functor'). Yet, in C++, a function _is_ not
an object.
In fact, you could ask yourself why the following statements aren't
true:
- Built-in types are classes. Class == type.
- Consequently, "int f() { ... }" is shorthand notation of "class {
int operator()() { ... } f;". I.e. functions are objects.
- The built-in type "void" is contentless, and as such cannot be
instantiated.
- void is the implicit ancestor of all classes, i.e:
int a;
void* p = &a; // Fine, int is superclass of void
int* pi = p; // Error, void is not an int
- There's an implicit class "main" surrounding your program. All
"global" objects are actually members of this implicit class. The
"main" class is implicitly instantiated when the program runs. main()
is the constructor of this class.
--
Christer Palm