Boost logo

Boost :

From: Paul Mensonides (pmenso57_at_[hidden])
Date: 2003-02-05 17:40:36


Peter Dimov wrote:

(Hi, Peter. I'm merging two different posts on this subject and replying to
both simultaneously.)

>>> Yes, exactly. Apologies for the confusion. It is a common mistake to
>>> provide only R T::* and expect that to match all pointers to
>>> members. To get back to your earlier post,
>>>
>>> int (X::*pf) () const;
>>>
>>> is not the same as
>>>
>>> typedef int (*F) () const;
>
> Should be typedef int F() const;
>
>>> since the first typedef is ill-formed, function types cannot be
>>> cv-qualified.
>
> That's 9.3/9, and yes, you are right, the typedef is legal.

Yes an no. The typedef is ill-formed, but not directly because of the
cv-qualification. To be technically accurate here, you cannot form a regular
pointer (or reference) to a cv-qualified function type. It is prohibited. I.e.

typedef int F() const; // legal

typedef int (*F)() const; // illegal
typedef int (&F)() const; // illegal

> I learn
> something new every day. Even though 3.9.3/1 implies that functions
> cannot
> be cv-qualified, and 8.3.5/4 says that creating a cv-qualified
> function type makes the program ill-formed, R T::* still matches a
> const member function, creating a const qualified function type as R.
> Or so Comeau says. C++ sure moves in mysterious ways.

Comeau is correct here. I'll spend a few words to clarify the subject though.
Normal cv-qualification is compounded from some other type, as in "const T." In
the case of function types however, cv-qualification is not compounded. It is
inherently part of the base function type or it isn't. So, from a certain
point-of-view, the above is accurate. You cannot take a function type and
cv-qualify it. However, the function type itself might already be cv-qualified.
In other words, cv-qualification on functions is not normal cv-qualification.
Specifically, it refers to the cv-qualification of the implicit this pointer.
E.g. something like this pseudo code:

typedef void f() const;

...roughly equivalent (in pseudocode) to this:

typedef void f(const class* this);

...which is why certain limitations exist on cv-qualified function types.
Specifically, they can only be used in three ways: 1) to declare a non-static
member function 2) to declare the pointed-to type of a pointer-to-member, and 3)
to declare another typedef. Note that in the first two cases (i.e. the cases of
actual use), the pseudo-type 'class' in the above can be deduced, either from
the enclosing class or from the pointer-to-member class type. Any other use is
illegal--such as forming a regular pointer or reference to a cv-qualified
function type. Then second case is interesting in the particular case of
detecting pointers-to-member-functions. The following is all legal:

typedef void F() const;

struct X {
    F f;
};

void X::f() const { }

F X::* pf = &X::f;

By the same token, the function type 'F' can be extracted from a
pointer-to-member-function with the specialization "R X::*" with 'R' being the
original function type:

#include <iostream>

template<class, class> struct is_same {
    enum { value = false };
};

template<class T> struct is_same<T, T> {
    enum { value = true };
};

template<class> struct extract;
template<class R, class C> struct extract<R C::*> {
    enum { value = is_same<R, F>::value };
};

int main() {
    std::cout
        << extract<F X::*>::value // 1
        << &std::endl;
    return 0;
}

Just for the record, however, Comeau doesn't get this whole area right (and many
other compilers don't get it right at all). Comeau doesn't consider the type
extracted from a pointer-to-member-function to be a regular function type.
Rather, it considers it to be some type of psuedo-function-type ala "member
function type"--which is incorrect. An example demonstrates this:

#include <iostream>
#include <typeinfo>

typedef void F();

struct X { };

typedef F X::* PMF;

template<class> struct extract;
template<class R, class C> struct extract<R C::*> {
    typedef R type;
};

extract<PMF>::type f; // decl: no error

void f() { return; } // def: error

int main() {
    std::cout
        << typeid(f).name() // compiler crash
                            // i.e. not an ICE
        << &std::endl;
    return 0;
}

I've been talking about this issue with Daveed Vandevoorde for the last couple
weeks, so EDG is aware of the issue.

Paul Mensonides


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk