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