Boost logo

Boost :

From: Paul Mensonides (pmenso57_at_[hidden])
Date: 2005-07-09 02:32:39


> -----Original Message-----
> From: boost-bounces_at_[hidden]
> [mailto:boost-bounces_at_[hidden]] On Behalf Of Tobias Schwinger

> It is not too clear whether it applies to partial template
> specialization, since
> we don't really "form" a type - we just consider a possibitity:
>
> typedef int my_const_function() const;
>
> template<typename T> struct remove_const {
> typedef T type; };
> template<typename T> struct remove_const< T const > {
> typedef T type; };
>
> // somwhere else
> remove_const<my_const_function>::type // ill-formed?

My main point in quoting the above is that "a cv-qualifier-seq in a function
declarator is not the same as adding cv-qualification on top of the function
type. i.e., it does not create a cv-qualified function type." Syntactically,
saying 'const T' where 'T' is a function type is applying cv-qualification to
the whole function type--which doesn't work. The cv-qualifiers on a function
type (such as 'int () const') are only an internal part of the composite
expression that forms a function type. Even syntactically, a cv-qualified
function type (in the sense of 'const T' could not be written 'int () const' in
the C++ syntax any more than 'int [] const' can be because of the wraparound
declarator syntax.

> >
> > There might be more references, but this gives a
> significantly strong
> > implication that cv-qualification on function types is
> radically different than
> > normal cv-qualification.
>
> ...and that cv-qualification on function types is intended
> for nonstatic member
> functions only (at least that's the impression I get from
> reading that paragraph).

The *types* cannot be used to declare anything else and no composite types
(except pointers-to-members) can be formed from them (e.g. you can't have a
regular pointer to a cv-qualified function type, such as 'int (*)() const').

> > The cv-qualifiers are modifying the implicit 'this'
> > parameter. I.e. the cv-qualifiers don't modify the
> function type (all functions
> > are 'const' because C++ doesn't treat functions as
> first-class objects); they
> > modify an implicit formal parameter. So attempting to
> remove cv-qualification
> > this way would be akin to transforming 'void (const int*)'
> into 'void (int*)'.
> >
>
> I don't find it very convincing -- we're talking about two
> different pairs of
> shoes here (syntax and type system vs. semantic treatment), IMO.

The syntax of the language, particularly the declarator syntax has no place for
cv-qualification on wraparound declarators (like function types and array
types). The trailing cv-qualifiers on a function type are a syntactic special
case, are interpreted in a special way in the type system, and receive special
semantic treatment. I.e. all parts of it are special cases that don't follow
the general rule.

> >>>struct C { };
> >>>
> >>>template<class T> struct is_const_function
> >>> : is_pointer_to_const_member_function<T C::*> { };
> >>>
>
> Another thought on this one: as you said already, before
> forming a type C::*T we
> have to make sure it works for the T we have. But can we do
> it with T actually
> being a type which renders our program ill-formed when
> encountered (and without
> relying on undefined behaviour)?

You'd have to specialize-out any type that can't be the subject of a
pointer-to-member. E.g. references are an example:

template<class T> struct is_const_function<T&> {
    static const bool value = false;
};

There aren't that many.

> > Yes. On the same token, however, I can just as easily
> write any other sort of
> > function type manipulation that I might need. The point is
> that a library like
> > yours should prevent the need for me to do it.
>
> Right. But is there a need for raw, cv-qualified functions in
> the first place?

I don't know. What is the need for manipulating pointer-to-member-function
types? The immediate difference is that pointers-to-members are class specific
(i.e. you have to supply the C::* part where C is some type). What if you're
passing them to something but want to apply the same function type to form
multiple pointers to members of different classes? I.e.

template<class T, class U, class F> class abc {
    typename return_type<F>::type func(F A::* p, F B::* q) {
        // ...
    }
};

Note that I'm just pulling this out of the magical land of make-believe where I
live. I don't directly have a need for this, but its something that someone
might need for some reason.

> However, I'm not concerned about preprocessing at all (I use
> preprocessed files).
> I also don't care about parsing time (it's comparatively
> little and it costs once
> per translation unit). What counts is the complexitiy of
> using the metafunctions.

Okay.

> I don't find your suggestion of testing C::*T instead of T
> generally unattractive
> (and ideally it might simplify things). The conditions for
> applying it would be:
>
> (when integrating)
> - be sure your suggestion builts on solid C++ (see above)

It is solid C++, it just isn't implemented correctly on several major C++
compilers.

> - find a nice name to disambiguate const and volatile
> property tags so I won't
> need an underscore suffix

I used to dispise them, but I've found that you get used to them fairly quickly,
and they just plain more convenient than any alternatives.

> (when the library is used)
> - it works with the compiler
> - the configuration allows it by specifying the same calling
> conventions for
> member and non member functions (and there is no MSVC
> __thiscall weirdness
> around)

#if !MSVC
    ... all related functionality ...
#endif

> I have to annotate a tiny bit of rationale here: int
> func(int...) can be invoked
> like func(1), so it should be a valid argument for a generic
> facility that accepts
> a unary callable argument which in turn has an int parameter.

I agree, but it's likely to be extremely rare. The entire concept of passing
(e.g.) a pointer to a variadic function to a generic facility is fraught with
problems.

> From what I have benchmarked so far it seems that adding (partial)
> specializations has a noticably smaller impact on the
> compilation time than adding
> instantiations (as required for decision making and matching
> these specializations
> twice).

What do you mean by, "as required for decision making and matching these
specializations twice"? What decisions (during the execution of the code) are
you talking about, and why would you need to match these specializations twice?

> I'm all for completeness but I'm not convinced that what you
> are proposing is in
> fact about completeness and not about emulating consistency
> where there is none.

It's not about emulation, its about mirroring all of the related facilities that
C++ has. Cv-unqualified function types are just a subset of function types as a
whole. They are a refinement that introduces more capabilities.

> Plus I'm wondering whether it's implementable in ISO C++ at all.

It is, but AFAIK they can only be matched/deduced with partial specializations.

Regards,
Paul Mensonides


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